package com.mzj.saas.mservice.property.service;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpUtil;
import com.mzj.saas.commons.*;
import com.mzj.saas.commons.enums.*;
import com.mzj.saas.commons.util.ConvertUtils;
import com.mzj.saas.commons.util.DataUtils;
import com.mzj.saas.commons.vo.redis.TokenRedisVo;
import com.mzj.saas.config.pay.PayConfig;
import com.mzj.saas.mservice.backlog.entity.BacklogItem;
import com.mzj.saas.mservice.backlog.entity.BacklogItemHouseRemaining;
import com.mzj.saas.mservice.backlog.entity.BacklogItemPropertyOtherBill;
import com.mzj.saas.mservice.backlog.entity.BacklogItemPropertyRentBill;
import com.mzj.saas.mservice.backlog.repository.BacklogItemHouseRemainingRepository;
import com.mzj.saas.mservice.backlog.repository.BacklogItemPropertyOtherBillRepository;
import com.mzj.saas.mservice.backlog.repository.BacklogItemPropertyRentBillRepository;
import com.mzj.saas.mservice.backlog.repository.BacklogItemRepository;
import com.mzj.saas.mservice.bill.mapper.ExpenseBillRowMapper;
import com.mzj.saas.mservice.bill.vo.*;
import com.mzj.saas.mservice.community.entity.Community;
import com.mzj.saas.mservice.community.entity.CommunityBuild;
import com.mzj.saas.mservice.community.entity.CommunityBuildUnit;
import com.mzj.saas.mservice.community.repository.CommunityBuildRepository;
import com.mzj.saas.mservice.community.repository.CommunityBuildUnitRepository;
import com.mzj.saas.mservice.community.repository.CommunityRepository;
import com.mzj.saas.mservice.department.entity.Department;
import com.mzj.saas.mservice.department.repository.DepartmentRepository;
import com.mzj.saas.mservice.eligible.entity.DepartmentHierarchy;
import com.mzj.saas.mservice.eligible.service.DepartmentHierarchyService;
import com.mzj.saas.mservice.property.entity.*;
import com.mzj.saas.mservice.property.repository.*;
import com.mzj.saas.mservice.property.util.ObjectConversion;
import com.mzj.saas.mservice.property.vo.*;
import com.mzj.saas.mservice.propertybill.entity.EstateViolateBill;
import com.mzj.saas.mservice.propertybill.repository.EstateViolateBillRepository;
import com.mzj.saas.mservice.propertybill.service.WithDrawService;
import com.mzj.saas.mservice.propertybill.vo.PenalSumVO;
import com.mzj.saas.mservice.sys.entity.AppUser;
import com.mzj.saas.mservice.sys.entity.User;
import com.mzj.saas.mservice.sys.entity.WechatAccount;
import com.mzj.saas.mservice.sys.repository.AppUserRepository;
import com.mzj.saas.mservice.sys.repository.UserRepository;
import com.mzj.saas.mservice.sys.repository.WechatAccountRepository;
import com.mzj.saas.redis.RedisService;
import com.mzj.saas.sdk.pay.alipay.AliPayRequest;
import com.mzj.saas.sdk.pay.vo.AliPay;
import com.mzj.saas.sdk.pay.vo.WxPay;
import com.mzj.saas.sdk.pay.wxpay.WxPayRequest;
import com.mzj.saas.sdk.util.WechatUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class EstateBillService {
    private static final Logger log = LoggerFactory.getLogger(EstateBillService.class);

    private static final String BILL_COUNT = "SELECT COUNT(1) ";
    private static final String BILL_COLUM = "SELECT DISTINCT e.id,e.`house_id` AS houseId,e.`community_id` AS communityId,e.`building_num` AS buildingNum,\n"
            + "e.`unit`,e.`room_number` AS roomNumber,e.`match_user_id` AS matchUserId,e.`charge_type` AS chargeType,\n"
            + "e.`measure_unit` AS measureUnit,e.`cost`,e.`start_time` AS startTime,e.`end_time` AS endTime,e.`payment_status` AS paymentStatus,\n"
            + "d.`water_start_num` AS waterStartNum,d.`water_end_num` AS waterEndNum,d.`water_usage` AS waterUsage,d.`water_unit_price` AS waterUnitPrice,\n"
            + "d.`electric_start_num` AS electricStartNum,d.`electric_end_num` AS electricEndNum,d.`electric_usage` AS electricUsage,d.`electric_unit_price` AS electricUnitPrice,\n"
            + "d.`gas_start_num` AS gasStartNum,d.`gas_end_num` AS gasEndNum,d.`gas_usage` AS gasUsage,d.`gas_unit_price` AS gasUnitPrice,\n"
            + "d.`heating_bill` AS heatingBill,d.`property_fee` AS propertyFee,d.`tv_fee` AS tvFee,d.`broad_band_charges` AS broadBandCharges,e.should_pay_time as shouldPayTime";
    private static final String BILL_FROM_MONITOR = " FROM `saas_estate_expense_bill` e INNER JOIN `saas_estate_expense_bill_detail` d ON e.id = d.`expense_bill_id` \n" +
            "left join (select * from saas_community_house_user where type != 3 and status = 1) u on e.house_id = u.community_house_id";
    private static final String BILL_ORDER = " ORDER BY e.create_time DESC";
    private static final String SQL_PAGE = " LIMIT ? OFFSET ?";

    private static final String ORDER_COUNT = "SELECT COUNT(1) ";
    private static final String ORDER_COLUM = "SELECT DISTINCT r.bill_id AS billId,r.bill_amount AS billAmount,r.business_type AS businessType,o.pay_time AS payTime,o.pay_result AS payResult,\n" +
            "b1.community_id as communityId1,b2.community_id AS communityId2,b1.building_num as buildingNum1,b2.building_num AS buildingNum2,\n" +
            "b1.unit as unit1,b2.unit AS unit2,b1.room_number as roomNumber1,b2.room_number as roomNumber2";
    private static final String ORDER_FROM_MONITOR = " FROM saas_estate_payment_order_relation r INNER JOIN saas_estate_payment_order o ON r.order_number = o.order_number\n" +
            "LEFT JOIN saas_estate_rent_bill b1 ON r.bill_id = b1.`id` "
            + "LEFT JOIN saas_estate_expense_bill b2 ON r.bill_id = b2.`id` "
            + "LEFT JOIN (SELECT * FROM saas_community_house_user hu WHERE hu.status = 1) hu ON b2.house_id = hu.community_house_id ";
    private static final String ORDER_ORDER = " ORDER BY o.pay_time DESC";
    private static final String CHARGE_TYPE_NAME = "余额充值";

    @Autowired
    private RedisService redisService;
    @Autowired
    private PayConfig payConfig;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private CommunityRepository communityRepository;
    @Autowired
    private CommunityBuildRepository communityBuildRepository;
    @Autowired
    private CommunityBuildUnitRepository communityBuildUnitRepository;
    @Autowired
    private AppUserRepository appUserRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private EstateRentBillRepository estateRentBillRepository;
    @Autowired
    private EstateExpenseBillRepository estateExpenseBillRepository;
    @Autowired
    private DepartmentHierarchyService departmentHierarchyService;
    @Autowired
    private WechatAccountRepository wechatAccountRepository;
    @Autowired
    @Qualifier("stringRedisTemplate")
    private StringRedisTemplate redisTemplate;
    @Autowired
    private EstatePaymentOrderRelationRepository estatePaymentOrderRelationRepository;
    @Autowired
    private EstatePaymentOrderRepository estatePaymentOrderRepository;
    @Autowired
    private WithDrawService withDrawService;
    @Autowired
    private HouseRemainingRepository houseRemainingRepository;
    @Autowired
    private HouseRemainingRecordRepository houseRemainingRecordRepository;
    @Autowired
    private HouseRemainingRelationRepository houseRemainingRelationRepository;
    @Autowired
    private CommunityHouseUserRepository communityHouseUserRepository;
    @Autowired
    private BacklogItemPropertyOtherBillRepository backlogItemPropertyOtherBillRepository;
    @Autowired
    private BacklogItemRepository backlogItemRepository;
    @Autowired
    private BacklogItemHouseRemainingRepository backlogItemHouseRemainingRepository;
    @Autowired
    private BacklogItemPropertyRentBillRepository backlogItemPropertyRentBillRepository;
    @Autowired
    private EstateViolateBillRepository estateViolateBillRepository;
    @Autowired
    private DepartmentRepository departmentRepository;
    @Autowired
    private MatchUserHouseRepository matchUserHouseRepository;
    @Autowired
    private MatchUserRepository matchUserRepository;

    @Value("${officialAccount.appId}")
    private String officialAccountAppId;

    @Value("${apartment.pay}")
    private boolean apartmentPay;

    public ResultBean<BillApiData<RentBillListVO>> rentBillList(String accessToken, String residenceCommunityId,
                                                                Long buildingNum, Long unit, String roomNumber, Integer pageNumber, Integer pageSize, Long id) {
        try {
            log.info("estateBill rentBillList,residenceCommunityId={},buildingNum={},unit={},roomNumber={},id={}", residenceCommunityId, buildingNum,
                    unit, roomNumber, id);
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            String userId = tokenRedisVo.getId();

            AppUser user = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);

            String sql = "select a.community_house_id as houseId  from saas_community_house_user a\n" +
                    "where a.status=1 and a.type != 3 and (a.mobile=" + user.getPhone() + " or a.user_id='" + userId + "')";
            List<RentBillListVO> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(RentBillListVO.class));
            List<String> houseIdList = list.stream().map(x -> x.getHouseId()).collect(Collectors.toList());

            BillApiData<RentBillListVO> apiData = new BillApiData<>();
            if (null == houseIdList || houseIdList.size() <= 0) {
                return ResultBean.successfulResult(apiData);
            }

            List<RentBillListVO> data = new ArrayList<>();
            long total = 0;

            if (id != null) {
                String rentBillSql = "select * from  saas_estate_rent_bill where  1=1 ";
                StringBuilder where = new StringBuilder();
                ArrayList<Object> arg = new ArrayList<>();

                where.append(" and payment_status=0 and audit_status=1 ");

                where.append(" and house_id in(    select a.community_house_id as houseId  from saas_community_house_user a\n" +
                        "    where a.status=1 and a.type != 3 and (a.mobile=" + user.getPhone() + " or a.user_id='" + userId + "')) ");

                if (StringUtils.isNotBlank(residenceCommunityId)) {
                    where.append(" and community_id = ? ");
                    arg.add(residenceCommunityId);
                }
                if (buildingNum != null) {
                    where.append(" and building_num = ? ");
                    arg.add(buildingNum);
                }
                if (unit != null) {
                    where.append(" and unit = ? ");
                    arg.add(unit);
                }
                if (StringUtils.isNotBlank(roomNumber)) {
                    where.append(" and room_number = ? ");
                    arg.add(roomNumber);

                }

                List<EstateRentBill> query = jdbcTemplate.query(rentBillSql + where, new BeanPropertyRowMapper<>(EstateRentBill.class), arg.toArray());
                total = query.size();
                if (total == 0) {
                    return ResultBean.successfulResult(apiData);
                }

                where.append(" ORDER BY FIELD(id, " + id + ") DESC ,create_time DESC ");

                arg.add(pageSize);
                arg.add(pageNumber * pageSize);

                List<EstateRentBill> estateRentBillList = jdbcTemplate.query(rentBillSql + where + SQL_PAGE, new BeanPropertyRowMapper<>(EstateRentBill.class), arg.toArray());

                for (EstateRentBill estateRentBill : estateRentBillList) {
                    data.add(convert(estateRentBill));
                }

            } else {
                Page<RentBillListVO> page = estateRentBillRepository.findAll((root, query, criteriaBuilder) -> {
                    Predicate predicate = criteriaBuilder.and();
                    predicate = criteriaBuilder.and(predicate, criteriaBuilder.in(root.get("houseId")).value(houseIdList));
                    predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("paymentStatus"), 0));
                    predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("auditStatus"), 1));

                    if (StringUtils.isNotBlank(residenceCommunityId)) {
                        predicate = criteriaBuilder.and(predicate,
                                criteriaBuilder.equal(root.get("communityId"), residenceCommunityId));
                    }
                    if (buildingNum != null) {
                        predicate = criteriaBuilder.and(predicate,
                                criteriaBuilder.equal(root.get("buildingNum"), buildingNum));
                    }
                    if (unit != null) {
                        predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("unit"), unit));
                    }
                    if (StringUtils.isNotBlank(roomNumber)) {
                        predicate = criteriaBuilder.and(predicate,
                                criteriaBuilder.equal(root.get("roomNumber"), roomNumber));
                    }

                    return predicate;
                }, PageRequest.of(pageNumber, pageSize, Sort.Direction.DESC, "createTime")).map(this::convert);

                total = page.getTotalElements();
                data = page.getContent();

                if (total == 0) {
                    return ResultBean.successfulResult(apiData);
                }
            }


            // 查询小区名称
            List<String> residenceCommunityIds = data.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(RentBillListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = data.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(RentBillListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = data.stream().filter(vo -> vo.getUnit() != null).map(RentBillListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }
            // 查询承租人名称
            List<String> userIds = data.stream().filter(vo -> vo.getMatchUserId() != null)
                    .map(RentBillListVO::getMatchUserId).distinct().collect(Collectors.toList());
            if (!userIds.isEmpty()) {
                List<AppUser> appUsers = appUserRepository.findByIdOrEnterpriseIn(userIds, userIds);
                for (AppUser appUser : appUsers) {
                    for (RentBillListVO vo : data) {
                        if (vo.getMatchUserId().equals(appUser.getId())
                                || vo.getMatchUserId().equals(appUser.getEnterpriseUserId())) {
                            vo.setMatchUserName(appUser.getName());
                        }
                    }
                }
            }

            Object object = redisService.getValue("platform.commission");
            Double d = (double) Integer.valueOf(object.toString()) / 100;
            BigDecimal commission = new BigDecimal(d.toString());// 手续费比例

            apiData.setTotal(total);
            apiData.setData(data);
            apiData.setCommission(commission);
            return ResultBean.successfulResult(apiData);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    private RentBillListVO convert(EstateRentBill entity) {
        RentBillListVO vo = new RentBillListVO();
        vo.setId(entity.getId());
        vo.setHouseId(entity.getHouseId());
        vo.setCommunityId(entity.getCommunityId());
        vo.setBuildingNum(entity.getBuildingNum());
        vo.setUnit(entity.getUnit());
        vo.setRoomNumber(entity.getRoomNumber());
        vo.setMatchUserId(entity.getMatchUserId());
        vo.setChargeType(entity.getChargeType());
        vo.setMeasureUnit(entity.getMeasureUnit());
        vo.setCost(ConvertUtils.fenConvertYuan(entity.getCost()));// 分转元
        vo.setShouldPayTime(DateUtils.format(DateUtils.getYesterday(entity.getStartTime()), DateUtils.FORMAT_DATE_8));// 有效期前一天
        vo.setStartTime(DateUtils.format(entity.getStartTime(), DateUtils.FORMAT_DATE_8));
        vo.setEndTime(DateUtils.format(entity.getEndTime(), DateUtils.FORMAT_DATE_8));
        vo.setPaymentStatus(entity.getPaymentStatus());
        return vo;
    }

    public ResultBean<RentBillDetailVO> rentBillDetail(Long id) {
        try {
            if (id == null) {
                return ResultBean.failedResultOfParamWithMsg("账单id为空");
            }

            Optional<EstateRentBill> optional = estateRentBillRepository.findById(id);
            if (!optional.isPresent()) {
                return ResultBean.failedResultWithMsg("账单不存在");
            }
            EstateRentBill estateRentBill = optional.get();

            RentBillDetailVO vo = new RentBillDetailVO();

            Optional<Community> communityOptional = communityRepository.findById(estateRentBill.getCommunityId());
            if (!communityOptional.isPresent()) {
                return ResultBean.failedResultWithMsg("小区不存在");
            }
            Community community = communityOptional.get();
            vo.setCommunityName(community.getName());

            Optional<CommunityBuild> buildOptional = communityBuildRepository.findById(estateRentBill.getBuildingNum());
            if (!buildOptional.isPresent()) {
                return ResultBean.failedResultWithMsg("楼栋不存在");
            }
            CommunityBuild build = buildOptional.get();
            vo.setBuildingName(build.getName());

            Optional<CommunityBuildUnit> unitOptional = communityBuildUnitRepository.findById(estateRentBill.getUnit());
            if (!unitOptional.isPresent()) {
                return ResultBean.failedResultWithMsg("单元不存在");
            }
            CommunityBuildUnit unit = unitOptional.get();
            vo.setUnitName(unit.getName());

            vo.setRoomNumber(estateRentBill.getRoomNumber());

            AppUser user = appUserRepository.findByIdOrEnterpriseUserId(estateRentBill.getMatchUserId(),
                    estateRentBill.getMatchUserId());
            if (user == null) {
                return ResultBean.failedResultWithMsg("用户不存在");
            }
            vo.setMatchUserName(user.getName());
            vo.setPhone(user.getPhone());

            vo.setChargeType(estateRentBill.getChargeType());
            Integer cost = estateRentBill.getCost();
            vo.setCost(ConvertUtils.fenConvertYuan(cost));
            vo.setShouldPayTime(
                    DateUtils.format(DateUtils.getYesterday(estateRentBill.getStartTime()), DateUtils.FORMAT_DATE_8));
            vo.setStartTime(DateUtils.format(estateRentBill.getStartTime(), DateUtils.FORMAT_DATE_8));
            vo.setEndTime(DateUtils.format(estateRentBill.getEndTime(), DateUtils.FORMAT_DATE_8));

            Object object = redisService.getValue("platform.commission");
            Double d = (double) Integer.valueOf(object.toString()) / 100;
            BigDecimal commission = new BigDecimal(d.toString());// 手续费比例

            BigDecimal commissionCost = ConvertUtils.fen2yuan(cost).multiply(commission);// 手续费
            vo.setCommissionCost(commissionCost.setScale(2).toString());

            BigDecimal amount = ConvertUtils.fen2yuan(cost).add(commissionCost);// 合计金额
            vo.setAmount(amount.setScale(2).toString());

            return ResultBean.successfulResult(vo);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<BillApiData<ExpenseBillListVO>> expenseBillList(String accessToken, String residenceCommunityId,
                                                                      Long buildingNum, Long unit, String roomNumber, Integer chargeType, Integer pageNumber, Integer pageSize, Long id) {
        log.info("estateBill expenseBillList,residenceCommunityId={},buildingNum={},unit={},roomNumber={},chargeType={},id={}", residenceCommunityId, buildingNum,
                unit, roomNumber, chargeType, id);
        ResultBean<BillApiData<ExpenseBillListVO>> resultBean = new ResultBean<>();
        resultBean.setCode("10000");
        resultBean.setMsg("10000");
        BillApiData<ExpenseBillListVO> apiData = new BillApiData<>();
        resultBean.setResultData(apiData);
        try {
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            String userId = tokenRedisVo.getId();


            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();
            where.append(" where 1 = 1");
            where.append(" and (e.`match_user_id` = ? or u.user_id = ? or u.mobile = ?)");
            args.add(userId);
            args.add(userId);

            // 通过登录用户手机号码也可以查询到账单
            Integer loginType = tokenRedisVo.getLoginType();
            String phone = null;
            if (loginType == 1) {
                User user = userRepository.findById(userId);
                phone = user.getMobile();
            } else {
                AppUser appUser = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);
                phone = appUser.getPhone();
            }
            args.add(phone);
            where.append(" and e.audit_status = 1 and e.`payment_status` = 0");

            if (!StringUtils.isBlank(residenceCommunityId)) {
                where.append(" and e.`community_id` = ?");
                args.add(residenceCommunityId);
            }
            if (buildingNum != null) {
                where.append(" and e.`building_num` = ?");
                args.add(buildingNum);
            }
            if (unit != null) {
                where.append(" and e.`unit` = ?");
                args.add(unit);
            }
            if (!StringUtils.isBlank(roomNumber)) {
                where.append(" and e.`room_number` = ?");
                args.add(roomNumber);
            }
            if (chargeType != null) {
                where.append(" and e.`charge_type` = ?");
                args.add(chargeType);
            }
            // System.out.println(BILL_COUNT + BILL_FROM_MONITOR + where);
            // 总数
            Long total = jdbcTemplate.queryForObject(BILL_COUNT + BILL_FROM_MONITOR + where, args.toArray(),
                    Long.class);
            if (total == 0) {
                return resultBean;
            }
            String billOrder = BILL_ORDER;
            if (id != null) {
                billOrder = " ORDER BY FIELD(e.id, ?) DESC, e.create_time DESC";
                args.add(id);
            }

            args.add(pageSize);
            args.add(pageNumber * pageSize);

            List<ExpenseBillListVO> data = jdbcTemplate.query(
                    BILL_COLUM + BILL_FROM_MONITOR + where + billOrder + SQL_PAGE, new ExpenseBillRowMapper(),
                    args.toArray());

            // 查询承租人名称
            List<String> ids = data.stream().filter(vo -> vo.getMatchUserId() != null)
                    .map(ExpenseBillListVO::getMatchUserId).distinct().collect(Collectors.toList());
            if (!ids.isEmpty()) {
                List<AppUser> appUsers = appUserRepository.findByIdOrEnterpriseIn(ids, ids);
                for (AppUser appUser : appUsers) {
                    for (ExpenseBillListVO vo : data) {
                        if (vo.getMatchUserId().equals(appUser.getId())
                                || vo.getMatchUserId().equals(appUser.getEnterpriseUserId())) {
                            vo.setMatchUserName(appUser.getName());
                        }
                    }
                }
            }
            // 查询小区名称
            List<String> residenceCommunityIds = data.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(ExpenseBillListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = data.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(ExpenseBillListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = data.stream().filter(vo -> vo.getUnit() != null).map(ExpenseBillListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }
            // 手续费比例
            Object object = redisService.getValue("platform.commission");
            Double d = (double) Integer.valueOf(object.toString()) / 100;
            BigDecimal commission = new BigDecimal(d.toString());

            // 去重
            data = data.stream().collect(
                    Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ExpenseBillListVO::getId))), ArrayList::new));

            apiData.setData(data);
            apiData.setTotal(total);
            apiData.setCommission(commission);
            resultBean.setResultData(apiData);
        } catch (Exception e) {
            log.error("EstateBillService expenseBillList Exception = {}" + e.getMessage(), e);
            resultBean.setCode("10001");
            resultBean.setMsg("10001");
        }
        resultBean.setResultData(apiData);
        return resultBean;
    }

    /**
     * 支付记录
     *
     * @param accessToken
     * @param businessType 账单类型
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public ResultBean<PaymentOrderApiData<PaymentOrderListVO>> paymentOrderList(String accessToken,
                                                                                Integer businessType, Integer pageNumber, Integer pageSize) {
        ResultBean<PaymentOrderApiData<PaymentOrderListVO>> resultBean = new ResultBean<>();
        resultBean.setCode("10000");
        resultBean.setMsg("10000");
        PaymentOrderApiData<PaymentOrderListVO> apiData = new PaymentOrderApiData<>();
        resultBean.setResultData(apiData);
        try {
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            String userId = tokenRedisVo.getId();
            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();
            where.append(" where 1 = 1");
            // where.append(" and (o.`pay_user_id` = ? OR hu.mobile = ?) ");
            where.append(" and (o.`pay_user_id` = ? )");
            args.add(userId);
            // 这段代码有问题，不能通过手机号查询账单
            // 通过登录用户手机号码也可以查询到账单
            // Integer loginType = tokenRedisVo.getLoginType();
            // String phone = null;
            // if (loginType == 1) {
            // User user = userRepository.findById(userId);
            // phone = user.getMobile();
            //} else {
            // AppUser appUser = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);
            // phone = appUser.getPhone();
            //}
            // args.add(phone);

            if (businessType != null) {
                where.append(" and r.`business_type` = ?");
                args.add(businessType);
            }
            System.out.println(ORDER_COUNT + ORDER_FROM_MONITOR + where);
            // 总数
            Long total = jdbcTemplate.queryForObject(ORDER_COUNT + ORDER_FROM_MONITOR + where, args.toArray(),
                    Long.class);
            if (total == 0) {
                return resultBean;
            }

            Long totalAmount = jdbcTemplate.queryForObject(
                    "SELECT SUM(r.bill_amount) AS totalAmount" + ORDER_FROM_MONITOR + where, args.toArray(),
                    Long.class);

            args.add(pageSize);
            args.add(pageNumber * pageSize);
            List<PaymentOrderListVO> data = jdbcTemplate.query(
                    ORDER_COLUM + ORDER_FROM_MONITOR + where + ORDER_ORDER + SQL_PAGE,
                    new BeanPropertyRowMapper<>(PaymentOrderListVO.class), args.toArray());
            System.out.println("查询sql --- " + ORDER_COLUM + ORDER_FROM_MONITOR + where + ORDER_ORDER + SQL_PAGE);

            for (PaymentOrderListVO vo : data) {
                vo.setBillAmount(ConvertUtils.fenConvertYuan(Long.parseLong(vo.getBillAmount())));
                vo.setPayTime(DateUtils.format(vo.getPayTime(), DateUtils.FORMAT_DATETIME_14, DateUtils.FORMAT_DATETIME_14));

                // 业务类型：1交房租 2交水费 3交电费 4交物业费 5交宽带费 6交电视费 7交煤气费 8交暖气费 9押金 10退租
                if (vo.getBusinessType() == 1 || vo.getBusinessType() == 9) {
                    vo.setCommunityId(vo.getCommunityId1());
                    vo.setBuildingNum(vo.getBuildingNum1());
                    vo.setUnit(vo.getUnit1());
                    vo.setRoomNumber(vo.getRoomNumber1());
                } else {
                    vo.setCommunityId(vo.getCommunityId2());
                    vo.setBuildingNum(vo.getBuildingNum2());
                    vo.setUnit(vo.getUnit2());
                    vo.setRoomNumber(vo.getRoomNumber2());
                }
            }

            // 查询小区名称
            List<String> residenceCommunityIds = data.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(PaymentOrderListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = data.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(PaymentOrderListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = data.stream().filter(vo -> vo.getUnit() != null).map(PaymentOrderListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }

            // 根据账单id去重
            data = data.stream().collect(
                    Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(PaymentOrderListVO::getBillId))), ArrayList::new));

            apiData.setData(data);
            apiData.setTotal(total);
            apiData.setTotalAmount(ConvertUtils.fenConvertYuan(totalAmount));
            resultBean.setResultData(apiData);
        } catch (Exception e) {
            log.error("EstateBillService paymentOrderList Exception = {}" + e.getMessage(), e);
            resultBean.setCode("10001");
            resultBean.setMsg("10001");
        }
        resultBean.setResultData(apiData);
        return resultBean;
    }

    /**
     * 物业-租金/押金app支付
     *
     * @param accessToken
     * @param vo
     * @return
     * @author shun
     * @date 2021年6月15日
     */
    public ResultBean<Map<String, Object>> payment(String accessToken, RentBillPaymentVo vo) {
        try {

            log.info("EstateBillService payment vo={}", vo.toString());

            if (CollectionUtils.isEmpty(vo.getIds())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (vo.getPaymentMode() == null || vo.getPaymentType() == null || vo.getChargeType() == null) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (!PaymentTypeEnum.operate(vo.getPaymentType()) || !PaymentModeEnum.operate(vo.getPaymentMode())
                    || !ChargeTypeEnum.operate(vo.getChargeType())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();
            List<EstateRentBill> rentBills = estateRentBillRepository.findByIdIn(vo.getIds());

            if (CollectionUtils.isEmpty(rentBills)) {
                log.warn("EstateBillService payment 未找到租金费用明细 id={}", vo.getIds());
                return ResultBean.failedResultWithMsg("未找到租金费用明细,请重试");
            }

            if (rentBills.size() != vo.getIds().size()) {
                log.warn("EstateBillService payment 查询数量不一致  ids={},bill={}", vo.getIds().size(), rentBills.size());
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            Integer cose = 0;
            for (EstateRentBill rentBill : rentBills) {
                if (rentBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("EstateBillService payment 此租金费用已支付,无需重复支付 id={},houseId={}", rentBill.getId(),
                            rentBill.getHouseId());
                    return ResultBean.failedResultWithMsg("当前有租金费用已支付,无需重复支付");
                }
                cose += rentBill.getCost();
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }

            Long orgId = rentBills.get(0).getOrgId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setIds(vo.getIds());
            redisVo.setTotalAmount(totalAmount);
            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金 5.生活缴费(房屋)
            redisVo.setBusinessType(4);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("EstateBillService payment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId, enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            // 0元支付
            if (DataUtils.doubleToInteger(totalAmount.doubleValue()) == 0) {
                return zeroPayment(redisVo);
            }

            // 支付方式
            if (vo.getPaymentType() == PaymentTypeEnum.WECHAT.getValue()) {
                WechatAccount account = null;
                // 根据企业设置, 获取收款方式(平台收款, 企业商户号收款)
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L, PaymentTypeEnum.WECHAT.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.WECHAT.getValue());
                }

                if (account == null) {
                    log.warn("EstateBillService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
                }
                // 应用ID或者分配的appId
                String appId = "";
                if (tokenRedisVo.getFlag() == 2) {
                    appId = redisService.getValue(Constant.WX_APPLET_APPID);
                } else if (tokenRedisVo.getFlag() == 3) {
                    appId = redisService.getValue(Constant.WX_APARTMENT_APPID);
                } else if (tokenRedisVo.getFlag() == 5) {
                    appId = officialAccountAppId;
                }


                String attac = outTradeNo;
                String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
                String body = "房屋管家-" + ChargeTypeEnum.getText(vo.getChargeType());
                cose = DataUtils.doubleToInteger(totalAmount.doubleValue());
                WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cose, body,
                        account.getMchApiKey(), null, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(),
                        spbillCreateIp);
                Map<String, Object> result = new WxPayRequest(wxpay).unifiedByApp();
                if (result == null) {
                    return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
                }
                redisVo.setMchApiKey(account.getMchApiKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }

            if (vo.getPaymentType() == PaymentTypeEnum.ALI.getValue()) {
                WechatAccount account = null;
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L, PaymentTypeEnum.ALI.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.ALI.getValue());
                }

                if (account == null) {
                    log.warn("EstateBillService payment 未找到支付宝配置,请联系负责人 orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到支付宝配置,请联系负责人");
                }
                String subject = ChargeTypeEnum.getText(vo.getChargeType());
                String passbackParams = outTradeNo;

                AliPay alipay = new AliPay(account.getMchId(), account.getPrivateKey(), account.getPublicKey(),
                        outTradeNo, totalAmount.toString(), subject, "QUICK_MSECURITY_PAY", passbackParams,
                        payConfig.getAliNotifyUrl());

                Map<String, Object> result = new AliPayRequest(alipay).appPay();

                if (result == null) {
                    return ResultBean.failedResultWithMsg("支付宝支付异常,请联系客服");
                }
                redisVo.setPublicKey(account.getPublicKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }
            return ResultBean.failedResultOfParamWithMsg("参数错误");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    /**
     * 物业房产-其它费用账单APP支付
     *
     * @param accessToken
     * @param vo
     * @return
     * @author shun
     * @date 2021年7月12日
     */
    public ResultBean<Map<String, Object>> otherPayment(String accessToken, RentBillPaymentVo vo) {
        try {
            log.info("EstateBillService otherPayment vo={}", vo.toString());

            // 租金/押金明细id
            if (CollectionUtils.isEmpty(vo.getIds())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (vo.getPaymentMode() == null || vo.getPaymentType() == null || vo.getChargeType() == null) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (!PaymentTypeEnum.operate(vo.getPaymentType()) || !PaymentModeEnum.operate(vo.getPaymentMode())
                    || !ChargeTypeEnum.operate(vo.getChargeType())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();
            List<EstateExpenseBill> estateBills = estateExpenseBillRepository.findByIdIn(vo.getIds());

            if (CollectionUtils.isEmpty(estateBills)) {
                log.warn("EstateBillService payment 未找到账单费用明细 id={}", vo.getIds());
                return ResultBean.failedResultWithMsg("未找到账单费用明细, 请重试");
            }

            if (estateBills.size() != vo.getIds().size()) {
                log.warn("EstateBillService payment 查询数量不一致 , ids={}, bill={}", vo.getIds().size(), estateBills.size());
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            Integer cose = 0;
            for (EstateExpenseBill rentBill : estateBills) {
                if (rentBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("EstateBillService payment 此账单费用已支付, 无需重复支付 id={}, houseId={}", rentBill.getId(),
                            rentBill.getHouseId());
                    return ResultBean.failedResultWithMsg("当前有账单费用已支付, 无需重复支付");
                }
                cose += rentBill.getCost();
            }

            // 手续费比例 5
            String platStr = redisService.getValue("platform.commission");
            // 账单
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                //*5 保留2位小数四舍五入
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }

            Long orgId = estateBills.get(0).getOrgId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setIds(vo.getIds());
            redisVo.setTotalAmount(totalAmount);
            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金 5.生活缴费 6公租房其它费用 7物业房产其它费用
            redisVo.setBusinessType(7);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("EstateBillService payment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId, enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            // 0元支付
            if (DataUtils.doubleToInteger(totalAmount.doubleValue()) == 0) {
                return zeroOtherPayment(redisVo);
            }

            // 支付方式
            if (vo.getPaymentType() == PaymentTypeEnum.WECHAT.getValue()) {
                WechatAccount account = null;
                // 根据企业设置, 获取收款方式(平台收款, 企业商户号收款)
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L, PaymentTypeEnum.WECHAT.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.WECHAT.getValue());
                }

                if (account == null) {
                    log.warn("EstateBillService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
                }
                // 应用ID或者分配的appId
                String appId = redisService.getValue(Constant.WX_APPID);
                String attac = outTradeNo;
                String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
                // 其它费用类型转换
                String chargeTypeName = "";
                if (vo.getChargeType() == 1) {
                    chargeTypeName = "水费";
                } else if (vo.getChargeType() == 2) {
                    chargeTypeName = "电费";
                } else if (vo.getChargeType() == 3) {
                    chargeTypeName = "物业费";
                } else if (vo.getChargeType() == 4) {
                    chargeTypeName = "宽带费";
                } else if (vo.getChargeType() == 5) {
                    chargeTypeName = "电视费";
                } else if (vo.getChargeType() == 6) {
                    chargeTypeName = "燃气费";
                } else if (vo.getChargeType() == 7) {
                    chargeTypeName = "暖气费";
                } else if (vo.getChargeType() == 12) {
                    chargeTypeName = "其它费用组合支付";
                }
                String body = "房屋管家-" + chargeTypeName;
                cose = DataUtils.doubleToInteger(totalAmount.doubleValue());
                WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cose, body,
                        account.getMchApiKey(), null, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(),
                        spbillCreateIp);
                Map<String, Object> result = new WxPayRequest(wxpay).unifiedByApp();
                if (result == null) {
                    return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
                }
                redisVo.setMchApiKey(account.getMchApiKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }

            if (vo.getPaymentType() == PaymentTypeEnum.ALI.getValue()) {
                WechatAccount account = null;
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L, PaymentTypeEnum.ALI.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.ALI.getValue());
                }

                if (account == null) {
                    log.warn("EstateBillService payment 未找到支付宝配置,请联系负责人 orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到支付宝配置,请联系负责人");
                }

                // 其它费用类型转换
                String chargeTypeName = "";
                if (vo.getChargeType() == 1) {
                    chargeTypeName = "水费";
                } else if (vo.getChargeType() == 2) {
                    chargeTypeName = "电费";
                } else if (vo.getChargeType() == 3) {
                    chargeTypeName = "物业费";
                } else if (vo.getChargeType() == 4) {
                    chargeTypeName = "宽带费";
                } else if (vo.getChargeType() == 5) {
                    chargeTypeName = "电视费";
                } else if (vo.getChargeType() == 6) {
                    chargeTypeName = "燃气费";
                } else if (vo.getChargeType() == 7) {
                    chargeTypeName = "暖气费";
                } else if (vo.getChargeType() == 12) {
                    chargeTypeName = "其它费用组合支付";
                }
                String subject = "房屋管家-" + chargeTypeName;
                String passbackParams = outTradeNo;

                AliPay alipay = new AliPay(account.getMchId(), account.getPrivateKey(), account.getPublicKey(),
                        outTradeNo, totalAmount.toString(), subject, "QUICK_MSECURITY_PAY", passbackParams,
                        payConfig.getAliNotifyUrl());

                Map<String, Object> result = new AliPayRequest(alipay).appPay();

                if (result == null) {
                    return ResultBean.failedResultWithMsg("支付宝支付异常,请联系客服");
                }
                redisVo.setPublicKey(account.getPublicKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }


            return ResultBean.failedResultOfParamWithMsg("参数错误");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }


    /**
     * 小程序-违约金账单支付
     *
     * @param accessToken
     * @param vo
     * @return
     * @author qixi
     * @date 2022年12月19日
     */
    public ResultBean<Map<String, Object>> appletOtherPayment(String accessToken, RentBillPaymentVo vo) {
        try {
            log.info("EstateBillService appletOtherPayment vo={}", vo.toString());


            if (vo.getPaymentMode() == null || vo.getChargeType() == null) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }


            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            Optional<AppUser> user = appUserRepository.findById(userId);
            if (!user.isPresent()) {
                return ResultBean.failedResultWithMsg("未查询到对应的用户");
            }
            String openId = user.get().getOpenId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();
            List<EstateViolateBill> estateBills = estateViolateBillRepository.findByIdIn(vo.getIds());

            if (CollectionUtils.isEmpty(estateBills)) {
                log.warn("EstateBillService payment 未找到违约金账单费用明细 id={}", vo.getIds());
                return ResultBean.failedResultWithMsg("未找到违约金账单费用明细, 请重试");
            }

            if (estateBills.size() != vo.getIds().size()) {
                log.warn("EstateBillService payment 查询数量不一致 , ids={}, bill={}", vo.getIds().size(), estateBills.size());
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            Integer cose = 0;
            for (EstateViolateBill rentBill : estateBills) {
                if (rentBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("EstateBillService payment 此账单费用已支付, 无需重复支付 id={}, houseId={}", rentBill.getId(),
                            rentBill.getHouseId());
                    return ResultBean.failedResultWithMsg("当前有账单费用已支付, 无需重复支付");
                }
                cose += rentBill.getCost();
            }

            // 手续费比例 5
            String platStr = redisService.getValue("platform.commission");
            // 账单
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                //*5 保留2位小数四舍五入
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }

            Long orgId = estateBills.get(0).getOrgId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setIds(vo.getIds());
            redisVo.setTotalAmount(totalAmount);
            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金 5.生活缴费 6公租房其它费用 7物业房产其它费用 13违约金
            redisVo.setBusinessType(13);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("EstateBillService payment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId, enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            // 0元支付
            if (DataUtils.doubleToInteger(totalAmount.doubleValue()) == 0) {
                return zeroViolatePayment(redisVo);
            }
            WechatAccount account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.WECHAT.getValue());
            if (account == null) {
                log.warn("EstateBillService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
            }
            // 应用ID或者分配的appId
            String appId = "";
            if (tokenRedisVo.getFlag() == 2) {
                appId = redisService.getValue(Constant.WX_APPLET_APPID);
            } else if (tokenRedisVo.getFlag() == 3) {
                appId = redisService.getValue(Constant.WX_APARTMENT_APPID);
            } else if (tokenRedisVo.getFlag() == 5) {
                appId = officialAccountAppId;
            }
            String attac = outTradeNo;
            String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);

            String body = "房屋管家-违约金";
            cose = DataUtils.doubleToInteger(totalAmount.doubleValue());
            WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cose, body,
                    account.getMchApiKey(), openId, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(),
                    spbillCreateIp);
            Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
            if (result == null) {
                return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
            }
            redisVo.setMchApiKey(account.getMchApiKey());
            redisVo.setMerchantId(account.getId());
            redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
            redisService.putPaymentBill(redisVo);
            return ResultBean.successfulResult(result);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<Map<String, Object>> appletPayment(String accessToken, RentBillPaymentVo vo) {
        try {

            log.info("EstateBillService payment vo={}", vo.toString());

            if (CollectionUtils.isEmpty(vo.getIds())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (vo.getPaymentMode() == null || vo.getPaymentType() == null || vo.getChargeType() == null) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (!PaymentTypeEnum.operate(vo.getPaymentType()) || !PaymentModeEnum.operate(vo.getPaymentMode())
                    || !ChargeTypeEnum.operate(vo.getChargeType())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            Optional<AppUser> user = appUserRepository.findById(userId);
            if (!user.isPresent()) {
                return ResultBean.failedResultWithMsg("用户不存在");
            }
            String openId = user.get().getOpenId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();
            List<EstateRentBill> rentBills = estateRentBillRepository.findByIdIn(vo.getIds());

            if (CollectionUtils.isEmpty(rentBills)) {
                log.warn("EstateBillService payment 未找到租金费用明细 id={}", vo.getIds());
                return ResultBean.failedResultWithMsg("未找到租金费用明细,请重试");
            }

            if (rentBills.size() != vo.getIds().size()) {
                log.warn("EstateBillService payment 查询数量不一致  ids={},bill={}", vo.getIds().size(), rentBills.size());
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            Integer cose = 0;
            for (EstateRentBill rentBill : rentBills) {
                if (rentBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("EstateBillService payment 此租金费用已支付,无需重复支付 id={},houseId={}", rentBill.getId(),
                            rentBill.getHouseId());
                    return ResultBean.failedResultWithMsg("当前有租金费用已支付,无需重复支付");
                }
                cose += rentBill.getCost();
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }

            Long orgId = rentBills.get(0).getOrgId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setIds(vo.getIds());
            redisVo.setTotalAmount(totalAmount);
            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金 5.生活缴费(房屋)
            redisVo.setBusinessType(4);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("EstateBillService payment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId, enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            // 0元支付
            if (DataUtils.doubleToInteger(totalAmount.doubleValue()) == 0) {
                return zeroPayment(redisVo);
            }
            WechatAccount account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.WECHAT.getValue());
            if (account == null) {
                log.warn("EstateBillService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
            }
            // 应用ID或者分配的appId
            String appId = "";
            if (tokenRedisVo.getFlag() == 2) {
                appId = redisService.getValue(Constant.WX_APPLET_APPID);
            } else if (tokenRedisVo.getFlag() == 3) {
                appId = redisService.getValue(Constant.WX_APARTMENT_APPID);
            } else if (tokenRedisVo.getFlag() == 5) {
                appId = officialAccountAppId;
            }
            String attac = outTradeNo;
            String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
            String body = "房屋管家-" + ChargeTypeEnum.getText(vo.getChargeType());
            cose = DataUtils.doubleToInteger(totalAmount.doubleValue());
            WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cose, body,
                    account.getMchApiKey(), openId, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(),
                    spbillCreateIp);
            Map<String, Object> result = new WxPayRequest(wxpay).unifiedByApp();
            if (result == null) {
                return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
            }
            redisVo.setMchApiKey(account.getMchApiKey());
            redisVo.setMerchantId(account.getId());
            redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
            redisService.putPaymentBill(redisVo);
            return ResultBean.successfulResult(result);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    /**
     * 小程序-余额充值支付
     *
     * @param accessToken
     * @param vo
     * @return
     * @author qixi
     * @date 2022年12月9日
     */
    public ResultBean<Map<String, Object>> payRemaining(String accessToken, RentBillPaymentRemainingVo vo) {
        try {
            log.info("EstateBillService RentBillPaymentRemainingVo vo={}", vo.toString());

            if (vo.getOrgId() == null || StringUtils.isEmpty(vo.getHouseId())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }
            if (vo.getCost() == null || vo.getCost() <= 0) {
                return ResultBean.failedResultOfParamWithMsg("金额错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            Long orgId = vo.getOrgId();

            String userId = tokenRedisVo.getId();
            String openId = null;
            Optional<AppUser> id = appUserRepository.findById(userId);

            HouseRemainingRelation relation = houseRemainingRelationRepository.findByOrgIdAndHouseId(vo.getOrgId(), vo.getHouseId());
            if (relation == null) {
                return ResultBean.failedResultOfParamWithMsg("当前房屋未出租");
            }

            // 账单
            BigDecimal totalAmount = BigDecimal.valueOf(vo.getCost()).setScale(2, BigDecimal.ROUND_HALF_UP);
            openId = id.get().getOpenId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(0);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setTotalAmount(totalAmount);
            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金 5.生活缴费 6公租房其它费用 7物业房产其它费用   12余额
            redisVo.setBusinessType(12);
            redisVo.setHouseRemainingId(relation.getRemainingId());

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("EstateBillService payment 未找到当前收款机构/企业 userId={},enterpriseId={}", userId, orgId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            WechatAccount account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.WECHAT.getValue());

            if (account == null) {
                log.warn("EstateBillService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
            }
            // 应用ID或者分配的appId
            String appId = "";
            if (tokenRedisVo.getFlag() == 2) {
                appId = redisService.getValue(Constant.WX_APPLET_APPID);
            } else if (tokenRedisVo.getFlag() == 3) {
                appId = redisService.getValue(Constant.WX_APARTMENT_APPID);
            } else if (tokenRedisVo.getFlag() == 5) {
                appId = officialAccountAppId;
            }
            String attac = outTradeNo;
            String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
            String body = "房屋管家-" + CHARGE_TYPE_NAME;

            Integer cost = DataUtils.doubleToInteger(totalAmount.doubleValue());

            WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cost, body,
                    account.getMchApiKey(), openId, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(),
                    spbillCreateIp);

            Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
            if (result == null) {
                return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
            }
            redisVo.setMchApiKey(account.getMchApiKey());
            redisVo.setMerchantId(account.getId());
            redisVo.setMerchantsType(1);
            redisService.putPaymentBill(redisVo);
            return ResultBean.successfulResult(result);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    // 查询账单支付明细
    public static void main(String[] args) {
        SortedMap<String, Object> paramsMap = new TreeMap<String, Object>();
        // appid
        paramsMap.put("appid", "wx8e37f936ae1c6a70");
        // 商户号
        paramsMap.put("mch_id", "1624653605");
        // 订单号
        paramsMap.put("transaction_id", "4200001980202310178568167040");
        // 随机支付串
        paramsMap.put("nonce_str", String.valueOf(KeyWorker.nextId()));
        // 签名
        String sign = WechatUtils.getSign(paramsMap, "123456qazwsxedcrfvtgbyhnujmiklop");
        paramsMap.put("sign", sign);
        // 请求参数转成xml字符串
        String xmlParams = XmlUtil.mapToXmlStr(paramsMap);

        String post = HttpUtil.post("https://api.mch.weixin.qq.com/pay/orderquery", xmlParams);
        System.out.println(post);


    }

    /**
     * 物业房产-余额查询  根据当前房屋获取物业以及产权单位余额账户以及余额变动记录 屏蔽未授权同住人
     *
     * @param accessToken
     * @param vo
     * @return
     * @author qixi
     * @date 2022年12月9日
     */
    public ResultBean<PaymentRemainingApiData<RemainingVo, HouseRemainingRecordVo>> findRemaining(String accessToken, BalanceVo vo) {
        try {
            log.info("EstateBillService BalanceVo vo={}", vo.toString());
            PaymentRemainingApiData<RemainingVo, HouseRemainingRecordVo> data = new PaymentRemainingApiData<>();

            List<PayOrgVo> orgVos = vo.getPayOrgVos();

            if (CollectionUtils.isEmpty(orgVos)) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            if (null == vo.getPageNumber())
                vo.setPageNumber(0);
            if (null == vo.getPageSize())
                vo.setPageSize(5);


            String userId = tokenRedisVo.getId();
            AppUser user = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);
            if (user == null) {
                return ResultBean.failedResultWithMsg("用户未注册");
            }

            String sql = "select a.community_house_id as houseId  from saas_community_house_user a\n" +
                    "where a.status=1 and a.type != 3 and (a.mobile=" + user.getPhone() + " or a.user_id='" + userId + "')";
            List<RentBillListVO> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(RentBillListVO.class));
            List<String> houseIdList = query.stream().map(x -> x.getHouseId()).collect(Collectors.toList());

            if (null == houseIdList || houseIdList.size() <= 0) {
                return ResultBean.successfulResult(data);
            }
            // 同住人是否有权限查看余额
            List<CommunityHouseUser> houseUsers = communityHouseUserRepository.findByUserIdAndCommunityHouseIdAndTypeAndStatus(tokenRedisVo.getId(), vo.getHouseId(), 2, 1);
            if (!CollectionUtils.isEmpty(houseUsers)) {
                for (CommunityHouseUser houseUser : houseUsers) {
                    if (tokenRedisVo.getId().equals(houseUser.getUserId()) && vo.getHouseId().equals(houseUser.getCommunityHouseId())) {
                        // 授权状态： 1已授权 2未授权
                        if (houseUser.getAccountStatus() == 2) {
                            return ResultBean.successfulResult(data);
                        }
                    }
                }
            }

            String remainingSql = "SELECT hr.id remainingId,hr.balance_fee totalAmount,rr.org_id orgId,dh.`name` orgName,hr.cash_pledge cashPledge,hr.status remainingStatus,hr.deferred_end_time deferredEndTime FROM `saas_house_remaining` hr\n" +
                    "LEFT JOIN saas_department_hierarchy dh ON hr.org_id = dh.org_id " +
                    "LEFT JOIN saas_house_remaining_relation rr ON rr.remaining_id = hr.id";

            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();

            where.append(" where 1=1 and rr.org_id in " + SQLUtils.foreachIn(orgVos.size()));
            orgVos.forEach(m -> args.add(m.getOrgId()));

            if (StringUtils.isNotBlank(vo.getHouseId())) {
                where.append(" and rr.house_id = ?");
                args.add(vo.getHouseId());
            }
            if (StringUtils.isNotBlank(vo.getMatchUserId())) {
                where.append("and hr.match_user_id = ?");
                args.add(vo.getMatchUserId());
            }
            // 余额记录
            List<RemainingVo> list = jdbcTemplate.query(remainingSql + where, new BeanPropertyRowMapper<>(RemainingVo.class), args.toArray());
            List<HouseRemainingRecordVo> totalData = new ArrayList<>();
            if (!CollectionUtils.isEmpty(list)) {
                for (RemainingVo pData : list) {
                    if (StringUtils.isNotBlank(pData.getDeferredEndTime()))
                        pData.setDeferredEndTime(DateUtils.convert(pData.getDeferredEndTime(), DateUtils.FORMAT_DATETIME_14).toString());
                    pData.setTotalAmount(ConvertUtils.fenConvertYuan(Integer.valueOf(pData.getTotalAmount())));
                    Long id = pData.getRemainingId();
                    Sort ageSort = Sort.by(Sort.Direction.DESC, "lastUpdateTime");
                    Page<HouseRemainingRecord> hList = houseRemainingRecordRepository.findByRemainingIdAndStatus(id, PaymentTypeEnum.SUCCESS.getValue(), PageRequest.of(vo.getPageNumber(), vo.getPageSize(), ageSort));
                    List<HouseRemainingRecordVo> hVos = ObjectConversion.copy(hList.getContent(), HouseRemainingRecordVo.class);
                    for (int i = 0; i < hVos.size(); i++) {
                        hVos.get(i).setOperateMoney(ConvertUtils.fenConvertYuan(Integer.valueOf(hVos.get(i).getOperateMoney())));
                        hVos.get(i).setOrgName(pData.getOrgName());
                        HouseRemainingRecord hrr = hList.getContent().get(i);
                    }
                    pData.setData(hVos);
                    totalData.addAll(hVos);
                    pData.setCashPledge(ConvertUtils.fenToYuan(pData.getCashPledge()));
                }
                totalData = totalData.stream().sorted(Comparator.comparing(HouseRemainingRecordVo::getLastUpdateTime).reversed()).collect(Collectors.toList());
                data.setTotalData(totalData);
                data.setData(list);
            }
            return ResultBean.successfulResult(data);
        } catch (Exception e) {
            log.error("EstateBillService findRemaining Exception = {}" + e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }


    /**
     * 小程序-未缴费明细  房屋未缴费状态账单明细 屏蔽未授权同住人
     *
     * @param accessToken
     * @param vo
     * @return
     * @author qixi
     * @date 2022年12月13日
     */
    public ResultBean<NotPaymentApiData<NotPaymentListVo>> notPayment(String accessToken, NotPaymentVo vo) {

        TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
        if (tokenRedisVo == null) {
            return ResultBean.failedResultOfToken();
        }
        NotPaymentApiData<NotPaymentListVo> apiData = new NotPaymentApiData<>();
        List<NotPaymentListVo> data = new ArrayList<>();

        if (null == vo.getPageNumber())
            vo.setPageNumber(1);
        if (null == vo.getPageSize())
            vo.setPageSize(10);

        // 同住人是否有权限查看
        List<CommunityHouseUser> houseUsers = communityHouseUserRepository.findByUserIdAndCommunityHouseIdAndTypeAndStatus(tokenRedisVo.getId(), vo.getHouseId(), 2, 1);
        if (!CollectionUtils.isEmpty(houseUsers)) {
            for (CommunityHouseUser houseUser : houseUsers) {
                if (tokenRedisVo.getId().equals(houseUser.getUserId()) && vo.getHouseId().equals(houseUser.getCommunityHouseId())) {
                    // 授权状态： 1已授权 2未授权
                    if (houseUser.getAccountStatus() == 2) {
                        return ResultBean.successfulResult(apiData);
                    }
                }
            }
        }

        if (null == tokenRedisVo.getCertificationStatus() || tokenRedisVo.getCertificationStatus() == 0) {
            return ResultBean.failedResultOfNotRealName();
        }
        String userId = tokenRedisVo.getId();

        AppUser user = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);
        if (user == null) {
            return ResultBean.failedResultWithMsg("用户未注册");
        }

        long total = 0;
        String sql = "select a.community_house_id as houseId  from saas_community_house_user a\n" +
                "where a.status=1 and a.type != 3 and (a.mobile=" + user.getPhone() + " or a.user_id='" + userId + "')";
        List<RentBillListVO> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(RentBillListVO.class));
        List<String> houseIdList = list.stream().map(x -> x.getHouseId()).collect(Collectors.toList());

        if (null == houseIdList || houseIdList.size() <= 0) {
            return ResultBean.successfulResult(apiData);
        }

        String rentBillSql = "SELECT  id as billId ,cost as billAmount ,if(charge_type = 1,8,9) as businessType,should_pay_time as shouldPayTime ,surplus_cost as surplusCost ," +
                "start_time as startTime,end_time as endTime ,org_id as orgId ,payment_status as paymentStatus , audit_status as auditStatus ,house_id as houseId ,community_id  as communityId ,match_user_id as matchUserId ," +
                "building_num as buildingNum ,unit,room_number as roomNumber ,create_time as createTime FROM `saas_estate_rent_bill` ";

        String otherBillSql = "SELECT  id ,cost as billAmount ,charge_type as businessType,should_pay_time as shouldPayTime ,surplus_cost as surplusCost ," +
                "start_time as startTime,end_time as endTime ,org_id as orgId ,payment_status as paymentStatus , audit_status as auditStatus ,house_id as houseId ,community_id  as communityId ,match_user_id as matchUserId ," +
                "building_num as buildingNum ,unit,room_number as roomNumber,create_time as createTime  FROM `saas_estate_expense_bill` ";

        otherBillSql = "select * from ( " + rentBillSql + "UNION ALL " + otherBillSql + ") a where 1=1 ";

        StringBuilder where = new StringBuilder();
        ArrayList<Object> arg = new ArrayList<>();
        StringBuilder where1 = new StringBuilder();
        StringBuilder where2 = new StringBuilder();
        ArrayList<Object> arg1 = new ArrayList<>();

        where.append(" and a.paymentStatus in (0,2) and a.auditStatus=1 ");
        where1.append(" and a.payment_status in (0,2) and a.audit_status=1 ");
        where2.append(" and a.adjust_status = 1 and a.audit_status=1 and b.`status` = 1  ");

        if (StringUtils.isNotBlank(vo.getHouseId())) {
            where.append(" and a.houseId = '" + vo.getHouseId() + "'");
            where1.append(" and a.house_id = '" + vo.getHouseId() + "'");
            where2.append(" and a.house_id = '" + vo.getHouseId() + "'");

            String matchUserId = null;
            List<MatchUserHouse> byHouseId = matchUserHouseRepository.findByHouseId(vo.getHouseId());
            if (!byHouseId.isEmpty()) {
                String matchUserStockId = byHouseId.get(0).getMatchUserStockId();
                //	根据承租人和房产的承租人id查询承认人表的房产承租人id
                Optional<MatchUser> matchUserRepositoryById = matchUserRepository.findById(Long.parseLong(matchUserStockId));
                if (matchUserRepositoryById.isPresent()) {
                    // 产权下的用户承租人id
                    MatchUser byMatchUserId = matchUserRepositoryById.get();
                    // 产权下的用户承租人id
                    matchUserId = byMatchUserId.getMatchUserId();
                }
            }
            where.append(" and a.matchUserId = ? ");
            where1.append(" and a.match_user_id = ? ");
            where2.append(" and a.match_user_id = ? ");
            arg.add(matchUserId);
        } else {
            return ResultBean.failedResultOfParamWithMsg("房屋数据有误");
        }


        if (StringUtils.isNotBlank(vo.getResidenceCommunityId())) {
            where.append(" and a.communityId = ? ");
            where1.append(" and a.community_id = ? ");
            where2.append(" and a.community_id = ? ");
            arg.add(vo.getResidenceCommunityId());
        }
        if (vo.getBuildingNum() != null) {
            where.append(" and a.buildingNum = ? ");
            where1.append(" and a.building_num = ? ");
            where2.append(" and a.building_num = ? ");
            arg.add(vo.getBuildingNum());
        }
        if (vo.getUnit() != null) {
            where.append(" and a.unit = ? ");
            where1.append(" and a.unit = ? ");
            where2.append(" and a.unit = ? ");
            arg.add(vo.getUnit());
        }
        if (StringUtils.isNotBlank(vo.getRoomNumber())) {
            where.append(" and a.roomNumber = ? ");
            where1.append(" and a.room_number = ? ");
            where2.append(" and a.room_number = ? ");
            arg.add(vo.getRoomNumber());
        }

        where.append(" ORDER BY a.createTime DESC ");
        where1.append(" ORDER BY a.create_time DESC ");

        String rentBillSumSql = "SELECT IFNULL(SUM( a.surplus_cost ),0) FROM `saas_estate_rent_bill` a where 1 = 1 ";
        String otherBillSumSql = "SELECT IFNULL(SUM( a.surplus_cost ),0) FROM `saas_estate_expense_bill` a where 1 = 1 ";
        int billAmount = 0;

        Integer rentBillAmount = jdbcTemplate.queryForObject(rentBillSumSql + where1, arg.toArray(), Integer.class);
        Integer otherBillAmount = jdbcTemplate.queryForObject(otherBillSumSql + where1, arg.toArray(), Integer.class);
        // bug 2312 费用交清后，首页未交费账单金额显示5元，添加条件 AND a.payment_status= 0
        Integer otherBillAdjust = jdbcTemplate.queryForObject("SELECT IFNULL(SUM( b.adjust_cost ),0) FROM `saas_estate_expense_bill` a LEFT JOIN saas_estate_adjust_bill b ON a.id = b.bill_id AND b.bill_type = 2 AND b.adjust_type = 0 AND a.payment_status= 0 where 1 = 1 " + where2, arg.toArray(), Integer.class);
        log.info("SELECT IFNULL(SUM( b.adjust_cost ),0) FROM `saas_estate_expense_bill` a LEFT JOIN saas_estate_adjust_bill b ON a.id = b.bill_id AND b.bill_type = 2 and b.adjust_type = 0 where 1 = 1 " + where2, arg.toArray());
        Integer rentBillAdjust = jdbcTemplate.queryForObject("SELECT IFNULL(SUM( b.adjust_cost ),0) FROM `saas_estate_rent_bill` a LEFT JOIN saas_estate_adjust_bill b ON a.id = b.bill_id AND b.bill_type = 1 AND b.adjust_type = 0 AND a.payment_status= 0 where 1 = 1 " + where2, arg.toArray(), Integer.class);
        billAmount = NumberUtil.add(rentBillAmount, otherBillAmount, rentBillAdjust, otherBillAdjust).intValue();


        List<NotPaymentListVo> vos = jdbcTemplate.query(otherBillSql + where, new BeanPropertyRowMapper<>(NotPaymentListVo.class), arg.toArray());

        if (vos.size() == 0 && billAmount == 0) {
            apiData.setData(data);
            apiData.setTotal(total);
            return ResultBean.successfulResult(apiData);
        }

        if (!CollectionUtils.isEmpty(vos)) {
            for (NotPaymentListVo vo1 : vos) {
                vo1.setBillAmount(ConvertUtils.fenToYuan(vo1.getBillAmount()));
                vo1.setSurplusCost(ConvertUtils.fenToYuan(vo1.getSurplusCost()));
                Optional<Department> byId = departmentRepository.findById(vo1.getOrgId());
                byId.ifPresent(department -> vo1.setOrgName(department.getName()));
                data.add(vo1);
            }
        }

        String totalAmount = ConvertUtils.fenConvertYuan(billAmount);
        apiData.setTotal((long) vos.size());
        apiData.setData(data);
        apiData.setTotalAmount(totalAmount);
        return ResultBean.successfulResult(apiData);
    }


    /**
     * 小程序-账单合计列表  本月所有账单合计 屏蔽未授权同住人
     *
     * @param accessToken
     * @author qixi
     * @date 2022年12月21日
     */
    public ResultBean<BillApiData<BillListVo>> billList(String accessToken, BillAllVo vo) {

        ResultBean<BillApiData<BillListVo>> resultBean = new ResultBean<>();
        resultBean.setCode("10000");
        resultBean.setMsg("10000");
        BillApiData<BillListVo> apiData = new BillApiData<>();
        resultBean.setResultData(apiData);
        try {
            if (vo.getPageNumber() == null) {
                vo.setPageNumber(0);
            }
            if (vo.getPageSize() == null) {
                vo.setPageSize(5);
            }
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            AppUser user = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);
            if (user == null) {
                return ResultBean.failedResultWithMsg("用户未注册");
            }

            String sql = "select a.community_house_id as houseId  from saas_community_house_user a\n" +
                    "where a.status=1 and a.type != 3 and (a.mobile=" + user.getPhone() + " or a.user_id='" + userId + "')";
            List<RentBillListVO> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(RentBillListVO.class));
            List<String> houseIdList = list.stream().map(x -> x.getHouseId()).collect(Collectors.toList());

            if (null == houseIdList || houseIdList.size() <= 0) {
                return ResultBean.successfulResult(apiData);
            }

            // 同住人是否有权限查看
            List<CommunityHouseUser> houseUsers = communityHouseUserRepository.findByUserIdAndCommunityHouseIdAndTypeAndStatus(tokenRedisVo.getId(), vo.getHouseId(), 2, 1);
            if (!CollectionUtils.isEmpty(houseUsers)) {
                for (CommunityHouseUser houseUser : houseUsers) {
                    if (tokenRedisVo.getId().equals(houseUser.getUserId()) && vo.getHouseId().equals(houseUser.getCommunityHouseId())) {
                        // 授权状态： 1已授权 2未授权
                        if (houseUser.getAccountStatus() == 2) {
                            return ResultBean.successfulResult(apiData);
                        }
                    }
                }
            }


            if (tokenRedisVo.getCertificationStatus() == 0 || tokenRedisVo.getCertificationStatus() == null) {
                return ResultBean.failedResultOfNotRealName();
            }

            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();

            StringBuilder where1 = new StringBuilder();
            List<Object> args1 = new ArrayList<>();

            where.append(" where 1 = 1  and a.auditStatus = 1 ");
            where1.append(" where 1 = 1  and v.auditStatus  = 1 ");

            String rentSql = "SELECT\n" +
                    "\teb.id,\n" +
                    "IF\n" +
                    "\t( eb.charge_type = 1, 8, 9 ) AS chargeType,\n" +
                    "\teb.create_time AS createTime,\n" +
                    "\teb.payment_time AS paymentTime,\n" +
                    "\teb.payment_status AS paymentStatus,\n" +
                    "\teb.cost,\n" +
                    "\teb.should_pay_time as shouldPayTime,\n" +
                    "\teb.surplus_cost AS surplusCost,\n" +
                    "\teb.start_time AS startTime,\n" +
                    "\teb.end_time AS endTime,\n" +
                    "\teb.house_id AS houseId,\n" +
                    "\teb.audit_status AS auditStatus,\n" +
                    "\teb.org_id AS orgId,\n" +
                    "\tdep.`name` AS orgName, \n" +
                    "\teb.`match_user_id` AS matchUserId \n" +
                    "FROM\n" +
                    "\tsaas_estate_rent_bill eb\n" +
                    "\tLEFT JOIN saas_department dep ON dep.id = eb.org_id";

            String expenseSql = "SELECT\n" +
                    "\teb.id,\n" +
                    "\teb.charge_type AS chargeType,\n" +
                    "\teb.create_time AS createTime,\n" +
                    "\teb.payment_time AS paymentTime,\n" +
                    "\teb.payment_status AS paymentStatus,\n" +
                    "\teb.cost,\n" +
                    "\teb.should_pay_time as shouldPayTime,\n" +
                    "\teb.surplus_cost AS surplusCost,\n" +
                    "\teb.start_time AS startTime,\n" +
                    "\teb.end_time AS endTime,\n" +
                    "\teb.house_id AS houseId,\n" +
                    "\teb.audit_status AS auditStatus,\n" +
                    "\teb.org_id AS orgId,\n" +
                    "\tdep.`name` AS orgName, \n" +
                    "\teb.`match_user_id` AS matchUserId \n" +
                    "FROM\n" +
                    "\tsaas_estate_expense_bill eb\n" +
                    "\tLEFT JOIN saas_department dep ON dep.id = eb.org_id ";

            String billSql = " select v.* ,eab.adjust_cost as adjustCost , eab.cost as adjustAfterCost , eab.status as adjustAfterStatus , " +
                    "eab.adjust_type as adjustType , eab.operator_id as adjustOperatorId ,eab.completion_time as adjustCompletionTime " +
                    "from ( " + rentSql + " UNION ALL " + expenseSql + " )  v " +
                    "LEFT JOIN saas_estate_adjust_bill eab ON v.id = eab.bill_id ";


            if (StringUtils.isNotBlank(vo.getHouseId())) {
                where.append(" and a.houseId = ? ");
                args.add(vo.getHouseId());

                where1.append(" and v.houseId = ? ");
                args1.add(vo.getHouseId());


                String matchUserId = null;
                List<MatchUserHouse> byHouseId = matchUserHouseRepository.findByHouseId(vo.getHouseId());
                if (!byHouseId.isEmpty()) {
                    String matchUserStockId = byHouseId.get(0).getMatchUserStockId();
                    //	根据承租人和房产的承租人id查询承认人表的房产承租人id
                    Optional<MatchUser> matchUserRepositoryById = matchUserRepository.findById(Long.parseLong(matchUserStockId));
                    if (matchUserRepositoryById.isPresent()) {
                        // 产权下的用户承租人id
                        MatchUser byMatchUserId = matchUserRepositoryById.get();
                        // 产权下的用户承租人id
                        matchUserId = byMatchUserId.getMatchUserId();
                    }
                }

                if (StrUtil.isNotBlank(matchUserId)) {
                    where.append(" and a.matchUserId = ? ");
                    args.add(matchUserId);

                    where1.append(" and v.matchUserId = ? ");
                    args1.add(matchUserId);
                }
            }

            if (StringUtils.isNotBlank(vo.getDateTim())) {
                Date date = DateUtils.convert(vo.getDateTim(), DateUtils.FORMAT_13);
                where.append(" and a.createTime between ? and ? ");
                String start = DateUtils.getFirstDayOfMonth1(date);
                String end = DateUtils.getLastDayOfMonth1(date);
                args.add(start);
                args.add(end);

                where1.append(" and v.createTime between ? and ? ");
                args1.add(start);
                args1.add(end);
            }

            where.append(" ORDER BY a.createTime DESC ");
            where1.append(" ORDER BY eab.completion_time DESC ");

            args.add(vo.getPageSize());
            args.add(vo.getPageNumber() * vo.getPageSize());
            args1.add(vo.getPageSize());
            args1.add(vo.getPageNumber() * vo.getPageSize());

            String billAll = "select * from(" + rentSql + " UNION ALL " + expenseSql + " ) a ";
            List<BillListVo> bill = jdbcTemplate.query(billAll + where + SQL_PAGE, new BeanPropertyRowMapper<>(BillListVo.class), args.toArray());
            List<BillListVo> adjustBill = jdbcTemplate.query(billSql + where1 + SQL_PAGE, new BeanPropertyRowMapper<>(BillListVo.class), args1.toArray());

            adjustBill.forEach(m -> {
                m.setExpense(ConvertUtils.fenConvertYuan(m.getCost()));
                m.setSurplusCost(ConvertUtils.fenConvertYuan(Integer.valueOf(m.getSurplusCost())));
                if (StringUtils.isNotEmpty(m.getAdjustAfterCost())) {
                    m.setAdjustAfterCost(ConvertUtils.fenToYuan(m.getAdjustAfterCost()));
                }
                if (StringUtils.isNotEmpty(m.getAdjustCost())) {
                    m.setAdjustAfterCost(ConvertUtils.fenToYuan(m.getAdjustCost()));
                }
                if (StringUtils.isNotEmpty(m.getAdjustOperatorId())) {
                    User houseUser = userRepository.findById(m.getAdjustOperatorId());
                    m.setAdjustOperatorName(houseUser.getName());
                }
            });
            bill.forEach(m -> {
                m.setExpense(ConvertUtils.fenConvertYuan(m.getCost()));
                m.setSurplusCost(ConvertUtils.fenConvertYuan(Integer.valueOf(m.getSurplusCost())));
            });

            apiData.setTotal((long) bill.size() + adjustBill.size());
            apiData.setData(bill);
            apiData.setAdjustData(adjustBill);
        } catch (Exception e) {
            log.error("EstateBillService billList Exception = {}" + e.getMessage(), e);
            resultBean.setCode("10001");
            resultBean.setMsg("10001");
        }
        resultBean.setResultData(apiData);
        return resultBean;
    }

    /**
     * 小程序-未缴费余额扣减
     *
     * @param accessToken
     * @author qixi
     * @date 2023年1月10日
     */
    public ResultBean<Object> remPayment(String accessToken, RentBillPaymentVo vo) {
        try {
            log.info("EstateBillService billList vo={}", vo.toString());

            if (vo.getEstateRentIds().size() + vo.getEstateExpenseIds().size() <= 0) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            List<Long> estateRentIds = vo.getEstateRentIds();
            List<Long> estateExpenseIds = vo.getEstateExpenseIds();
            List<EstateRentBill> rentBills = estateRentBillRepository.findByIdIn(estateRentIds);
            List<EstateExpenseBill> expenseBills = estateExpenseBillRepository.findByIdIn(estateExpenseIds);

            /**
             * 优先判断总金额和余额账户是否够扣减  不够直接返回余额不足
             */
            Integer cost = 0;
            Integer remainingCost = 0;
            Long orgId = 0L;
            if (!CollectionUtils.isEmpty(rentBills)) {
                for (EstateRentBill rentBill : rentBills) {
                    cost += rentBill.getSurplusCost();
                    if (!orgId.equals(rentBill.getOrgId())) {
                        orgId = rentBill.getOrgId();
                        HouseRemainingRelation remRelation = houseRemainingRelationRepository.findByOrgIdAndHouseId(orgId, rentBill.getHouseId());
                        if (remRelation == null) {
                            return ResultBean.failedResultOfParamWithMsg("余额账户不存在");
                        }
                        HouseRemaining remaining = houseRemainingRepository.findById(remRelation.getRemainingId()).get();
                        remainingCost += remaining.getBalanceFee();
                    }
                }
            }
            if (!CollectionUtils.isEmpty(expenseBills)) {
                for (EstateExpenseBill expenseBill : expenseBills) {
                    cost += expenseBill.getSurplusCost();
                    if (!orgId.equals(expenseBill.getOrgId())) {
                        orgId = expenseBill.getOrgId();
                        HouseRemainingRelation remRelation = houseRemainingRelationRepository.findByOrgIdAndHouseId(orgId, expenseBill.getHouseId());
                        if (remRelation == null) {
                            return ResultBean.failedResultOfParamWithMsg("余额账户不存在");
                        }
                        HouseRemaining remaining = houseRemainingRepository.findById(remRelation.getRemainingId()).get();
                        remainingCost += remaining.getBalanceFee();
                    }
                }
            }
            if (cost > remainingCost)
                return ResultBean.failedResultWithMsg("账户余额不足");

            // 扣减余额
            if (!CollectionUtils.isEmpty(rentBills))
                deductionRentBill(rentBills);
            if (!CollectionUtils.isEmpty(expenseBills))
                deductionExpenseBill(expenseBills);
            return ResultBean.successfulResult("支付成功");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    /**
     * 小程序-未缴费余额扣减
     *
     * @param accessToken
     * @author qixi
     * @date 2023年1月10日
     */
    public ResultBean<Map<String, Object>> billRemainingPayment(String accessToken, RentBillPaymentVo vo) {
        try {
            log.info("EstateBillService billList vo={}", vo.toString());

            List<Long> estateRentIds = vo.getEstateRentIds();
            List<Long> estateExpenseIds = vo.getEstateExpenseIds();
            if (CollectionUtils.isEmpty(estateRentIds) && CollectionUtils.isEmpty(estateExpenseIds)) {
                return ResultBean.failedResultOfParamWithMsg("账单id不能为空");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            Long orgId = null;
            List<EstateRentBill> rentBills = new ArrayList<>();
            HouseRemainingRelation rel = new HouseRemainingRelation();
            if (!CollectionUtils.isEmpty(estateRentIds)) {
                rentBills = estateRentBillRepository.findByIdIn(estateRentIds);
                if (rentBills.size() != vo.getEstateRentIds().size()) {
                    return ResultBean.failedResultOfParamWithMsg("账单数量不匹配");
                }
                EstateRentBill estateRentBill = rentBills.get(0);
                orgId = estateRentBill.getOrgId();
                rel = houseRemainingRelationRepository.findByOrgIdAndHouseId(estateRentBill.getOrgId(), estateRentBill.getHouseId());
            }

            List<EstateExpenseBill> expenseBills = new ArrayList<>();
            if (!CollectionUtils.isEmpty(estateExpenseIds)) {
                expenseBills = estateExpenseBillRepository.findByIdIn(estateExpenseIds);
                if (expenseBills.size() != vo.getEstateExpenseIds().size()) {
                    return ResultBean.failedResultOfParamWithMsg("账单数量不匹配");
                }
                EstateExpenseBill estateExpenseBill = expenseBills.get(0);
                orgId = estateExpenseBill.getOrgId();
                rel = houseRemainingRelationRepository.findByOrgIdAndHouseId(orgId, estateExpenseBill.getHouseId());
            }

            if (rel == null) {
                return ResultBean.failedResultWithMsg("账户未找到");
            }


            String userId = tokenRedisVo.getId();
            Optional<AppUser> user = appUserRepository.findById(userId);
            if (!user.isPresent()) {
                return ResultBean.failedResultWithMsg("用户不存在");
            }
            String openId = user.get().getOpenId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();


            List<Long> orgIds = rentBills.stream().map(EstateRentBill::getOrgId).distinct().collect(Collectors.toList());
            expenseBills.stream().map(EstateExpenseBill::getOrgId).distinct().forEach(orgIds::add);
            orgIds = orgIds.stream().distinct().collect(Collectors.toList());
            if (orgIds.size() > 1) {
                return ResultBean.failedResultOfParamWithMsg("账单不属于同一机构");
            }

            Integer cose = 0;
            for (EstateRentBill rentBill : rentBills) {
                if (rentBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("EstateBillService payment 此租金费用已支付,无需重复支付 id={},houseId={}", rentBill.getId(),
                            rentBill.getHouseId());
                    return ResultBean.failedResultWithMsg("当前有租金费用已支付,无需重复支付");
                }
                cose += rentBill.getSurplusCost();
            }

            for (EstateExpenseBill expenseBill : expenseBills) {
                if (expenseBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    return ResultBean.failedResultWithMsg("当前有账单已支付,无需重复支付");
                }
                cose += expenseBill.getSurplusCost();
            }


            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }


            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setEstateRentIds(estateRentIds);
            redisVo.setEstateExpenseIds(estateExpenseIds);
            redisVo.setTotalAmount(totalAmount);
            redisVo.setHouseRemainingId(rel.getRemainingId());

            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金 5.生活缴费(房屋)
            redisVo.setBusinessType(15);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("EstateBillService payment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId, enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            WechatAccount account = wechatAccountRepository.findByOrgIdAndType(orgId, PaymentTypeEnum.WECHAT.getValue());
            if (account == null) {
                log.warn("EstateBillService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
            }
            // 应用ID或者分配的appId
            String appId = "";
            if (tokenRedisVo.getFlag() == 2) {
                appId = redisService.getValue(Constant.WX_APPLET_APPID);
            } else if (tokenRedisVo.getFlag() == 3) {
                appId = redisService.getValue(Constant.WX_APARTMENT_APPID);
            } else if (tokenRedisVo.getFlag() == 5) {
                appId = officialAccountAppId;
            }
            String attac = outTradeNo;
            String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
            String body = "房屋管家-账单余额缴费";
            cose = DataUtils.doubleToInteger(totalAmount.doubleValue());

            WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, apartmentPay ? cose : 1, body,
                    account.getMchApiKey(), openId, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(),
                    spbillCreateIp);

            Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
            if (result == null) {
                return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
            }
            redisVo.setMchApiKey(account.getMchApiKey());
            redisVo.setMerchantId(account.getId());
            redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
            redisService.putPaymentBill(redisVo);
            return ResultBean.successfulResult(result);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    /**
     * 其他费用缴费
     *
     * @param expenseBills
     * @param
     * @return
     */
    public void deductionExpenseBill(List<EstateExpenseBill> expenseBills) {
        Long orgId = 0L;
        String houseId = "";
        if (!CollectionUtils.isEmpty(expenseBills)) {
            for (EstateExpenseBill expenseBill : expenseBills) {
                orgId = expenseBill.getOrgId();
                houseId = expenseBill.getHouseId();
                HouseRemainingRelation rel = houseRemainingRelationRepository.findByOrgIdAndHouseId(orgId, houseId);
                // 完成缴费
                deductionRemaining(rel.getRemainingId(), expenseBill.getId(), expenseBill.getChargeType());
            }
        }

    }

    /**
     * 租金押金缴费
     *
     * @param estateRentBills
     * @param
     * @return
     */
    public void deductionRentBill(List<EstateRentBill> estateRentBills) {
        Long orgId = 0L;
        String houseId = "";
        if (!CollectionUtils.isEmpty(estateRentBills)) {
            for (EstateRentBill expenseBill : estateRentBills) {
                orgId = expenseBill.getOrgId();
                houseId = expenseBill.getHouseId();
                HouseRemainingRelation rel = houseRemainingRelationRepository.findByOrgIdAndHouseId(orgId, houseId);
                // 完成缴费
                deductionRemaining(rel.getRemainingId(), expenseBill.getId(), expenseBill.getChargeType() == 1 ? 8 : 9);
            }
        }
    }

    /**
     * 扣减余额
     *
     * @param remainingId 余额id
     * @param billId      账单id
     * @param chargeType  扣款类型 1水费 2电费 3物业费 4宽带费 5电视费 6煤气费 7暖气费 8租金 9押金
     */
    @Transactional
    public Integer deductionRemaining(Long remainingId, Long billId, Integer chargeType) {
        Integer flag = DeductionEnum.NOT_PAY.getValue();
        Optional<HouseRemaining> rem = houseRemainingRepository.findById(remainingId);
        if (!rem.isPresent()) {
            log.info("未查到余额账户 {}", remainingId);
            return flag;
        }
        HouseRemaining houseRemaining = rem.get();
        Integer balanceFee = houseRemaining.getBalanceFee();
        // 操作金额
        Integer operateMoney = 0;
        BigDecimal remaining = new BigDecimal(balanceFee);
        final Date updateTime = new Date();
        // 系统id
        String systemUser = redisService.getSystemUser();
        if (balanceFee <= 0) {
            log.info("余额不足 {}", remainingId);
            return flag;
        }
        if (chargeType != 8 && chargeType != 9) {// 其他费用
            Optional<EstateExpenseBill> expenseBill = estateExpenseBillRepository.findById(billId);
            EstateExpenseBill estateExpenseBill = expenseBill.get();
            Integer cost = estateExpenseBill.getSurplusCost();
            BigDecimal billCost = new BigDecimal(cost);
            operateMoney = cost;
            // 账单费用扣减
            cost = Integer.parseInt(remaining.subtract(billCost).toString());
            estateExpenseBill.setSurplusCost(0);
            estateExpenseBill.setPaymentTime(updateTime);
            estateExpenseBill.setOperatorId(systemUser);
            estateExpenseBill.setPaymentChannel(PaymentTypeEnum.Remaining.getValue());
            estateExpenseBill.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
            EstateExpenseBill saveBill = estateExpenseBillRepository.save(estateExpenseBill);

            // 余额扣减
            houseRemaining.setBalanceFee(cost);
            houseRemaining.setLastUpdateTime(updateTime);
            HouseRemaining saveRemaining = houseRemainingRepository.save(houseRemaining);

            // 余额操作记录
            HouseRemainingRecord record = new HouseRemainingRecord();
            record.setBillId(saveBill.getId());
            record.setRemainingId(saveRemaining.getId());
            record.setOperateMoney(operateMoney);
            record.setOperatorId(systemUser);
            record.setOperateName(SystemEnum.SYSTEM_USER_NAME.getValue());
            record.setLastUpdateTime(updateTime);
            record.setUpdateStatus(PaymentTypeEnum.SUCCESS.getValue());
            record.setCause(PaymentCauseEnum.SYSTEM_DEDUCTION.getValue());
            record.setOperateStatus(PaymentTypeEnum.BUCKLE.getValue());
            record.setBillStatus(PaymentTypeEnum.EXPENSE_BILL.getValue());
            // 状态：1正常 2已删除
            record.setStatus(1);
            houseRemainingRecordRepository.save(record);


            // 余额变动待办事项
            houseRemainingItem(saveBill.getHouseId(), saveBill.getMatchUserId(), saveRemaining.getId(), chargeType, operateMoney, systemUser, saveBill.getOrgId());
            // 其他费用待办事项
            propertyOtherItem(saveBill.getId());

            flag = DeductionEnum.ALL_PAY.getValue();

        } else {// 租金押金
            Optional<EstateRentBill> rentBill = estateRentBillRepository.findById(billId);
            EstateRentBill estateRentBill = rentBill.get();
            Integer cost = estateRentBill.getSurplusCost();
            BigDecimal billCost = new BigDecimal(cost);

            // 1租金 2押金
            Integer rentBillChargeType = estateRentBill.getChargeType();
            if (rentBillChargeType == 1) {// 租金
                operateMoney = cost;
                // 账单费用扣减
                cost = Integer.parseInt(remaining.subtract(billCost).toString());
                estateRentBill.setSurplusCost(0);
                estateRentBill.setPaymentTime(updateTime);
                estateRentBill.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                EstateRentBill saveBill = estateRentBillRepository.save(estateRentBill);

                // 余额扣减
                houseRemaining.setBalanceFee(cost);
                houseRemaining.setLastUpdateTime(updateTime);
                HouseRemaining saveRemaining = houseRemainingRepository.save(houseRemaining);

                // 余额操作记录
                HouseRemainingRecord record = new HouseRemainingRecord();
                record.setBillId(saveBill.getId());
                record.setRemainingId(saveRemaining.getId());
                record.setOperateMoney(operateMoney);
                record.setOperatorId(systemUser);
                record.setOperateName(SystemEnum.SYSTEM_USER_NAME.getValue());
                record.setLastUpdateTime(updateTime);
                record.setUpdateStatus(PaymentTypeEnum.SUCCESS.getValue());
                record.setCause(PaymentCauseEnum.SYSTEM_DEDUCTION.getValue());
                record.setOperateStatus(PaymentTypeEnum.BUCKLE.getValue());
                record.setBillStatus(PaymentTypeEnum.RENT_BILL.getValue());
                // 状态：1正常 2已删除
                record.setStatus(1);
                houseRemainingRecordRepository.save(record);

                // 余额变动待办事项
                houseRemainingItem(saveBill.getHouseId(), saveBill.getMatchUserId(), saveRemaining.getId(), chargeType, operateMoney, systemUser, saveBill.getOrgId());
                // 租金/押金待办事项
                propertyRentItem(saveBill.getId());
                flag = DeductionEnum.ALL_PAY.getValue();

            } else {
                // 押金
                operateMoney = cost;
                // 账单费用扣减
                cost = Integer.parseInt(remaining.subtract(billCost).toString());
                estateRentBill.setSurplusCost(0);
                estateRentBill.setPaymentTime(updateTime);
                estateRentBill.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                EstateRentBill saveBill = estateRentBillRepository.save(estateRentBill);

                // 余额扣减
                houseRemaining.setBalanceFee(cost);
                houseRemaining.setCashPledge(operateMoney);
                houseRemaining.setLastUpdateTime(updateTime);
                HouseRemaining saveRemaining = houseRemainingRepository.save(houseRemaining);

                // 余额操作记录
                HouseRemainingRecord record = new HouseRemainingRecord();
                record.setBillId(saveBill.getId());
                record.setRemainingId(saveRemaining.getId());
                record.setOperateMoney(operateMoney);
                record.setOperatorId(systemUser);
                record.setOperateName(SystemEnum.SYSTEM_USER_NAME.getValue());
                record.setLastUpdateTime(updateTime);
                record.setUpdateStatus(PaymentTypeEnum.SUCCESS.getValue());
                record.setCause(PaymentCauseEnum.SYSTEM_DEDUCTION.getValue());
                record.setOperateStatus(PaymentTypeEnum.BUCKLE.getValue());
                record.setBillStatus(PaymentTypeEnum.DEPOSIT_BILL.getValue());
                // 状态：1正常 2已删除
                record.setStatus(1);
                houseRemainingRecordRepository.save(record);

                // 余额变动待办事项
                houseRemainingItem(saveBill.getHouseId(), saveBill.getMatchUserId(), saveRemaining.getId(), chargeType, operateMoney, systemUser, saveBill.getOrgId());
                // 租金/押金待办事项
                propertyRentItem(saveBill.getId());
                flag = DeductionEnum.ALL_PAY.getValue();

            }

        }
        return flag;
    }

    /**
     * 余额扣款待办事项
     *
     * @param houseId     房屋ID
     * @param matchUserId 承租人ID
     * @param remainingId 余额ID
     * @param chargeType  扣款类型 1水费 2电费 3物业费 4宽带费 5电视费 6煤气费 7暖气费 8租金 9押金
     * @param cost        金额
     * @param operatorId  操作人ID
     * @param orgId       收款机构ID
     */
    public void houseRemainingItem(String houseId, String matchUserId, Long remainingId, Integer chargeType, Integer cost, String operatorId, Long orgId) {
        String yuan = ConvertUtils.fenToYuan(String.valueOf(cost));
        String chargeName = null;
        switch (chargeType) {  //  1水费 2电费 3物业费 4宽带费 5电视费 6煤气费 7暖气费 8租金 9押金
            case 1:
                chargeName = "水费";
                break;
            case 2:
                chargeName = "电费";
                break;
            case 3:
                chargeName = "物业费";
                break;
            case 4:
                chargeName = "宽带费";
                break;
            case 5:
                chargeName = "电视费";
                break;
            case 6:
                chargeName = "燃气费";
                break;
            case 7:
                chargeName = "暖气费";
                break;
            case 8:
                chargeName = "租金";
                break;
            case 9:
                chargeName = "押金";
                break;
        }

//        String sql="select * from (\n" +
//                "select a.mobile as mobile ,a.community_house_id as houseId  , a.user_id as userId  from  saas_community_house_user a\n" +
//                "where   a.status = 1 \n" +
//                "UNION\n" +
//                "select b.phone as mobile , a.house_id as houseId , a.match_user_id as userId from   saas_house_match_user a \n" +
//                "LEFT JOIN saas_app_user b on b.id= a.match_user_id\n" +
//                "where 1=1\n" +
//                "UNION \n" +
//                "select b.mobile as mobile , a.house_id as houseId , c.id as userId from   saas_house_match_user a \n" +
//                "LEFT JOIN saas_house_eligible_apply_cohabit b on b.apply_id= a.apply_id\n" +
//                "LEFT JOIN saas_app_user c on c.phone = b.mobile \n" +
//                "where 1=1 and b.mobile is not null \n" +
//                ") t1 where t1.houseId ='"+houseId+"' ";

        /**
         * qixi
         * 2022-12-28 11:32
         * 余额变动承租人和已授权账单的同住人可见
         *
         */
        String sql = "select * from (select a.mobile as mobile ,a.community_house_id as houseId  , a.user_id as userId  " +
                "from  saas_community_house_user a\n" +
                "where a.status = 1 and a.type = 1  or (a.type = 2 and a.account_status = 1)\n" +
                "UNION\n" +
                "select b.phone as mobile , a.house_id as houseId , a.match_user_id as userId from   saas_house_match_user a\n" +
                "LEFT JOIN saas_app_user b on b.id= a.match_user_id\n" +
                "where 1=1\n" +
                ") t1 where t1.houseId ='" + houseId + "' ";


        String where = " and 1=1 ";

        List<CommunityHouseUser> CommunityHouseUserList = jdbcTemplate.query(sql + where, new BeanPropertyRowMapper<>(CommunityHouseUser.class));

        CommunityHouseUserList = CommunityHouseUserList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(CommunityHouseUser::getMobile))), ArrayList::new));
        for (CommunityHouseUser communityHouseUser : CommunityHouseUserList) {
            // 账单扣款余额变动
            BacklogItem backlogItem = new BacklogItem();
            backlogItem.setHouseId(houseId);
            backlogItem.setOrgId(orgId);
            backlogItem.setType(BacklogItemTypeEnum.REMAINING_CHANGE.getValue());
            backlogItem.setTitle("余额变动");
            backlogItem.setContent(chargeName + "账单，系统扣款：" + yuan + "元");
            backlogItem.setBusinessStatus("待查看");
            backlogItem.setHandleStatus(0);
            backlogItem.setOperatorId(operatorId);// 操作人id
            backlogItem.setMatchUserId(matchUserId);
            backlogItem.setPhone(communityHouseUser.getMobile());
            backlogItem.setCreateTime(new Date());
            BacklogItem save = backlogItemRepository.save(backlogItem);

            BacklogItemHouseRemaining houseRemaining = new BacklogItemHouseRemaining();
            houseRemaining.setBacklogItemId(save.getId());
            houseRemaining.setRemainingId(remainingId);
            backlogItemHouseRemainingRepository.save(houseRemaining);

        }
    }


    /**
     * 非公租房其他账单待办事项
     */
    public void propertyOtherItem(Long billId) {
        List<BacklogItemPropertyOtherBill> propertyOtherBill = backlogItemPropertyOtherBillRepository.findByBillId(billId);
        if (propertyOtherBill != null && propertyOtherBill.size() > 0) {
            Date date = new Date();
            for (BacklogItemPropertyOtherBill bill : propertyOtherBill) {
                Optional<BacklogItem> itemOpt = backlogItemRepository.findById(bill.getBacklogItemId());
                if (itemOpt.isPresent()) {
                    BacklogItem item = itemOpt.get();
                    item.setBusinessStatus("已缴费");
                    item.setHandleStatus(1);
                    item.setHandlerTime(date);
                    backlogItemRepository.save(item);
                }
            }
        }

    }

    /**
     * 非公租房租金待办事项
     */
    public void propertyRentItem(Long billId) {
        List<BacklogItemPropertyRentBill> propertyRentBill = backlogItemPropertyRentBillRepository.findByRentBillIdOrDepositBillId(billId, billId);
        if (propertyRentBill != null && propertyRentBill.size() > 0) {
            Date date = new Date();
            for (BacklogItemPropertyRentBill bill : propertyRentBill) {
                Optional<BacklogItem> itemOpt = backlogItemRepository.findById(bill.getBacklogItemId());
                if (itemOpt.isPresent()) {
                    BacklogItem item = itemOpt.get();
                    item.setBusinessStatus("已缴费");
                    item.setHandleStatus(1);
                    item.setHandlerTime(date);
                    backlogItemRepository.save(item);
                }
            }
        }
    }


    public ResultBean<Map<String, Object>> zeroPayment(PaymentBillRedisVo redisVo) {
        try {
            ResultBean<Map<String, Object>> resultBean = new ResultBean<>();
            resultBean.setCode("10009");
            resultBean.setMsg("0元支付成功");

            List<EstateRentBill> bills = estateRentBillRepository.findByIdIn(redisVo.getIds());
            if (CollectionUtils.isEmpty(bills)) {
                log.error("zeroPayment, 未找到相关费用信息 ids={}", redisVo.getIds());
                return ResultBean.failedResultWithMsg("未找到相关费用信息");
            }

            Date payTime = new Date();
            Integer billAmount = 0;

            List<EstatePaymentOrderRelation> porList = new ArrayList<>();
            for (EstateRentBill b : bills) {
                if (b.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    continue;
                }
                // 修改状态为已缴费
                b.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                b.setPaymentTime(payTime);
                b.setPaymentChannel(1);
                billAmount += b.getCost();

                EstatePaymentOrderRelation por = new EstatePaymentOrderRelation();
                por.setOrderNumber(redisVo.getOutTradeNo());
                por.setBillId(b.getId());
                por.setPlatformCommissionAmount(redisVo.getPlat());
                // por.setMerchantId(redisVo.getMerchantId());
                por.setBillAmount(b.getCost());
                if (b.getChargeType() == 1) {
                    por.setBusinessType(BusinessTypeEnum.RENT_FEE.getValue());
                } else if (b.getChargeType() == 2) {
                    por.setBusinessType(BusinessTypeEnum.DEPOSIT_FEE.getValue());
                }
                porList.add(por);
            }

            // 更新租金/押金账单明细支付状态
            estateRentBillRepository.saveAll(bills);
            // 新增账单与支付关系明细
            estatePaymentOrderRelationRepository.saveAll(porList);

            // 新增账单支付记录
            EstatePaymentOrder paymentOrder = new EstatePaymentOrder();
            paymentOrder.setBillAmount(billAmount);
            paymentOrder.setOrderNumber(redisVo.getOutTradeNo());
            paymentOrder.setPayType(1);
            paymentOrder.setPayMode(redisVo.getPaymentMode());
            paymentOrder.setPayTime(payTime);
            paymentOrder.setPlatformCommissionAmount(redisVo.getPlat());
            // 手续费 千分之6
            Integer payHandlingFee = new BigDecimal(billAmount * 0.006).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            paymentOrder.setWxCommissionAmount(payHandlingFee);
            paymentOrder.setPayUserId(redisVo.getUserId());
            paymentOrder.setTotalAmount(0);
            paymentOrder.setPayResult(1);
            estatePaymentOrderRepository.save(paymentOrder);

            return resultBean;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<Map<String, Object>> zeroOtherPayment(PaymentBillRedisVo redisVo) {
        try {
            ResultBean<Map<String, Object>> resultBean = new ResultBean<>();
            resultBean.setCode("10009");
            resultBean.setMsg("0元支付成功");

            List<EstateExpenseBill> expenseBills = estateExpenseBillRepository.findByIdIn(redisVo.getIds());
            if (CollectionUtils.isEmpty(expenseBills)) {
                log.error("zeroPayment, 未找到相关费用信息 ids={}", redisVo.getIds());
                return ResultBean.failedResultWithMsg("未找到相关费用信息");
            }

            Date payTime = new Date();
            Integer billAmount = 0;

            List<EstatePaymentOrderRelation> porList = new ArrayList<>();
            for (EstateExpenseBill b : expenseBills) {
                if (b.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    continue;
                }
                // 修改状态为已缴费
                b.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                b.setPaymentTime(payTime);
                b.setPaymentChannel(1);
                billAmount += b.getCost();

                EstatePaymentOrderRelation por = new EstatePaymentOrderRelation();
                por.setOrderNumber(redisVo.getOutTradeNo());
                por.setBillId(b.getId());
                por.setPlatformCommissionAmount(redisVo.getPlat());
                por.setMerchantId(redisVo.getMerchantId());
                por.setBillAmount(b.getCost());
                // 其它费用类型转换
                if (b.getChargeType() == 1) {
                    por.setBusinessType(BusinessTypeEnum.WATER_FEE.getValue());
                } else if (b.getChargeType() == 2) {
                    por.setBusinessType(BusinessTypeEnum.ELECTRIC_FEE.getValue());
                } else if (b.getChargeType() == 3) {
                    por.setBusinessType(BusinessTypeEnum.PROPERTY_FEE.getValue());
                } else if (b.getChargeType() == 4) {
                    por.setBusinessType(BusinessTypeEnum.BROADBAND_FEE.getValue());
                } else if (b.getChargeType() == 5) {
                    por.setBusinessType(BusinessTypeEnum.TV_FEE.getValue());
                } else if (b.getChargeType() == 6) {
                    por.setBusinessType(BusinessTypeEnum.COALGAS_FEE.getValue());
                } else if (b.getChargeType() == 7) {
                    por.setBusinessType(BusinessTypeEnum.HEATING_FEE.getValue());
                } else if (b.getChargeType() == 10) {
                    // 退租
                    por.setBusinessType(BusinessTypeEnum.OTHER_FEE.getValue());
                    // 更新退租账单状态
                    withDrawService.changeStatus(b.getId(), b.getHouseId());
                }
                porList.add(por);
            }

            // 更新账单明细支付状态
            estateExpenseBillRepository.saveAll(expenseBills);
            estatePaymentOrderRelationRepository.saveAll(porList);

            // 新增账单支付记录
            EstatePaymentOrder paymentOrder = new EstatePaymentOrder();
            paymentOrder.setBillAmount(billAmount);
            paymentOrder.setOrderNumber(redisVo.getOutTradeNo());
            paymentOrder.setPayType(2);
            paymentOrder.setPayMode(redisVo.getPaymentMode());
            paymentOrder.setPayTime(payTime);
            paymentOrder.setPlatformCommissionAmount(redisVo.getPlat());
            // 手续费 千分之6
            Integer payHandlingFee = new BigDecimal(billAmount * 0.006).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            paymentOrder.setWxCommissionAmount(payHandlingFee);
            paymentOrder.setPayUserId(redisVo.getUserId());
            paymentOrder.setTotalAmount(0);
            paymentOrder.setPayResult(1);
            estatePaymentOrderRepository.save(paymentOrder);

            return resultBean;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<Map<String, Object>> zeroViolatePayment(PaymentBillRedisVo redisVo) {
        try {
            ResultBean<Map<String, Object>> resultBean = new ResultBean<>();
            resultBean.setCode("10009");
            resultBean.setMsg("0元支付成功");

            List<EstateViolateBill> expenseBills = estateViolateBillRepository.findByIdIn(redisVo.getIds());
            if (CollectionUtils.isEmpty(expenseBills)) {
                log.error("zeroPayment, 未找到相关费用信息 ids={}", redisVo.getIds());
                return ResultBean.failedResultWithMsg("未找到相关费用信息");
            }

            Date payTime = new Date();
            Integer billAmount = 0;

            List<EstatePaymentOrderRelation> porList = new ArrayList<>();
            for (EstateViolateBill b : expenseBills) {
                if (b.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    continue;
                }
                // 修改状态为已缴费
                b.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                b.setPaymentTime(payTime);
                billAmount += b.getCost();

                EstatePaymentOrderRelation por = new EstatePaymentOrderRelation();
                por.setOrderNumber(redisVo.getOutTradeNo());
                por.setBillId(b.getId());
                por.setPlatformCommissionAmount(redisVo.getPlat());
                por.setMerchantId(redisVo.getMerchantId());
                por.setBillAmount(b.getCost());
                por.setBusinessType(11);
                porList.add(por);
            }

            // 更新账单明细支付状态
            estateViolateBillRepository.saveAll(expenseBills);
            estatePaymentOrderRelationRepository.saveAll(porList);

            for (Long id : redisVo.getIds()) {
                propertyOtherItem(id);
            }


            // 新增账单支付记录
            EstatePaymentOrder paymentOrder = new EstatePaymentOrder();
            paymentOrder.setBillAmount(billAmount);
            paymentOrder.setOrderNumber(redisVo.getOutTradeNo());
            paymentOrder.setPayType(2);
            paymentOrder.setPayMode(redisVo.getPaymentMode());
            paymentOrder.setPayTime(payTime);
            paymentOrder.setPlatformCommissionAmount(redisVo.getPlat());
            // 手续费 千分之6
            Integer payHandlingFee = new BigDecimal(billAmount * 0.006).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            paymentOrder.setWxCommissionAmount(payHandlingFee);
            paymentOrder.setPayUserId(redisVo.getUserId());
            paymentOrder.setTotalAmount(0);
            paymentOrder.setPayResult(1);
            estatePaymentOrderRepository.save(paymentOrder);

            return resultBean;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    /**
     * 小程序-违约金列表
     *
     * @param accessToken
     * @author qixi
     * @date 2022年12月21日
     */
    public ResultBean<ApiData<PenalSumVO>> violateList(String accessToken, BillAllVo vo) {
        try {
            TokenRedisVo token = redisService.findTokenVo(accessToken);
            if (token == null) {
                return ResultBean.failedResultOfToken();
            }
            Integer flag = token.getFlag();
            ApiData<PenalSumVO> data = new ApiData<>();
            List<PenalSumVO> list = new ArrayList<>();
            data.setData(list);
            data.setTotal(0L);

            if (!StringUtils.isNotBlank(vo.getHouseId())) {
                return ResultBean.failedResultOfParamWithMsg("参数有误");
            }

            // 同住人是否有权限查看
            List<CommunityHouseUser> houseUsers = communityHouseUserRepository.findByUserIdAndCommunityHouseIdAndTypeAndStatus(token.getId(), vo.getHouseId(), 2, 1);
            if (!CollectionUtils.isEmpty(houseUsers)) {
                for (CommunityHouseUser houseUser : houseUsers) {
                    if (token.getId().equals(houseUser.getUserId()) && vo.getHouseId().equals(houseUser.getCommunityHouseId())) {
                        // 授权状态： 1已授权 2未授权
                        if (houseUser.getAccountStatus() == 2) {
                            return ResultBean.successfulResult(data);
                        }
                    }
                }
            }

            // 租金
            String rentSql = "SELECT\n" +
                    "\tb.`id`  as billId,\n" +
                    "\tb.`house_id` AS houseId,\n" +
                    "\tb.`community_id` AS communityId,\n" +
                    "\tb.`building_num` AS buildingNum,\n" +
                    "\tb.`unit`,\n" +
                    "\tb.`room_number` AS roomNumber,\n" +
                    "\tb.`org_id` AS orgId,\n" +
                    "\tb.`match_user_id` AS matchUserId,\n" +
                    "\tvb.`bill_type` AS chargeType,\n" +
                    "\tb.`measure_unit` AS measureUnit,\n" +
                    "\tb.`cost` as billCost,\n" +
                    "\tb.`start_time` AS startTime,\n" +
                    "\tb.`end_time` AS endTime,\n" +
                    "\tb.`create_time` AS createTime,\n" +
                    "\tb.should_pay_time AS shouldPayTime,\n" +
                    "\tb.surplus_cost AS billSurplusCost,\n" +
                    "\t2 AS houseType,(\n" +
                    "\tSELECT\n" +
                    "\t\ti.create_time \n" +
                    "\tFROM\n" +
                    "\t\tsaas_backlog_item_bill ib\n" +
                    "\t\tLEFT JOIN `saas_backlog_item` i ON i.`id` = ib.backlog_item_id \n" +
                    "\tWHERE\n" +
                    "\t\tib.`bill_id` = b.id \n" +
                    "\tGROUP BY\n" +
                    "\t\ti.create_time DESC \n" +
                    "\t\tLIMIT 1 \n" +
                    "\t) AS itemTime,\n" +
                    "\tIFNULL( u.NAME, ui.NAME ) AS matchUserName,\n" +
                    "\tIFNULL( u.mobile, ui.phone ) AS phone,\n" +
                    "\tbr.collection_org AS collectionOrg,\n" +
                    "\tvb.growth_cost as growthCost,\n" +
                    "\tvb.cost as penalSumCost,\n" +
                    "\tvb.create_time as penalSumTime,\n" +
                    "\tvb.payment_time as paymentTime,\n" +
                    "\tvb.payment_status as paymentStatus,vb.operator_id as operatorId,\n" +
                    "\tvb.operator_time as operatorTime,\n" +
                    "\tvb.`status` as violateBillStatus,\n" +
                    "\tvb.id as violateId  ,vb.lease_status as leaseStatus  \n" +
                    "FROM\n" +
                    "  (SELECT * FROM saas_estate_violate_bill WHERE bill_type in (8,9) ) as  vb\n" +
                    "\tLEFT JOIN `saas_estate_rent_bill` b ON vb.bill_id = b.id\n" +
                    "\tLEFT JOIN `saas_app_user` a1 ON a1.`id` = b.match_user_id\n" +
                    "\tLEFT JOIN `saas_community_house_user` u ON u.`community_house_id` = b.house_id \n" +
                    "\tAND u.STATUS = 1 \n" +
                    "\tAND u.type = 1\n" +
                    "\tLEFT JOIN (\n" +
                    "\tSELECT\n" +
                    "\t\ta.house_id,\n" +
                    "\t\tb.NAME,\n" +
                    "\t\tc.phone \n" +
                    "\tFROM\n" +
                    "\t\tsaas_house_match_user a\n" +
                    "\t\tLEFT JOIN saas_house_eligible_apply b ON b.id = a.apply_id\n" +
                    "\t\tLEFT JOIN saas_app_user c ON c.id = a.match_user_id \n" +
                    "\tWHERE\n" +
                    "\t\ta.STATUS = 1 \n" +
                    "\t) ui ON ui.`house_id` = b.house_id\n" +
                    "\tLEFT JOIN saas_house_expense_bill_rule br ON b.rule_id = br.id\n";
            // 账单
            String billSql = "SELECT\n" +
                    "\tb.`id`  as billId,\n" +
                    "\tb.`house_id` AS houseId,\n" +
                    "\tb.`community_id` AS communityId,\n" +
                    "\tb.`building_num` AS buildingNum,\n" +
                    "\tb.`unit`,\n" +
                    "\tb.`room_number` AS roomNumber,\n" +
                    "\tb.`org_id` AS orgId,\n" +
                    "\tb.`match_user_id` AS matchUserId,\n" +
                    "\tvb.`bill_type`  AS chargeType,\n" +
                    "\tb.`measure_unit` AS measureUnit,\n" +
                    "\tb.`cost` as billCost,\n" +
                    "\tb.`start_time` AS startTime,\n" +
                    "\tb.`end_time` AS endTime,\n" +
                    "\tb.`create_time` AS createTime,\n" +
                    "\tb.should_pay_time AS shouldPayTime,\n" +
                    "\tb.surplus_cost AS billSurplusCost,\n" +
                    "\t2 AS houseType,(\n" +
                    "\tSELECT\n" +
                    "\t\ti.create_time \n" +
                    "\tFROM\n" +
                    "\t\tsaas_backlog_item_bill ib\n" +
                    "\t\tLEFT JOIN `saas_backlog_item` i ON i.`id` = ib.backlog_item_id \n" +
                    "\tWHERE\n" +
                    "\t\tib.`bill_id` = b.id \n" +
                    "\tGROUP BY\n" +
                    "\t\ti.create_time DESC \n" +
                    "\t\tLIMIT 1 \n" +
                    "\t) AS itemTime,\n" +
                    "\tIFNULL( u.NAME, ui.NAME ) AS matchUserName,\n" +
                    "\tIFNULL( u.mobile, ui.phone ) AS phone,\n" +
                    "\tbr.collection_org AS collectionOrg,\n" +
                    "\tvb.growth_cost as growthCost,\n" +
                    "\tvb.cost as penalSumCost,\n" +
                    "\tvb.create_time as penalSumTime,\n" +
                    "\tvb.payment_time as paymentTime,\n" +
                    "\tvb.payment_status as paymentStatus ,vb.operator_id as operatorId,\n" +
                    "\tvb.operator_time as operatorTime,\n" +
                    "\tvb.`status` as violateBillStatus,\n" +
                    "\tvb.id as violateId ,vb.lease_status as leaseStatus  \n" +
                    "FROM\n" +
                    "  (SELECT * FROM saas_estate_violate_bill WHERE bill_type in (1,2,3,4,5,6,7) ) as  vb\n" +
                    "\tLEFT JOIN `saas_estate_expense_bill` b ON vb.bill_id = b.id\n" +
                    "\tLEFT JOIN `saas_app_user` a1 ON a1.`id` = b.match_user_id\n" +
                    "\tLEFT JOIN `saas_community_house_user` u ON u.`community_house_id` = b.house_id \n" +
                    "\tAND u.STATUS = 1 \n" +
                    "\tAND u.type = 1\n" +
                    "\tLEFT JOIN (\n" +
                    "\tSELECT\n" +
                    "\t\ta.house_id,\n" +
                    "\t\tb.NAME,\n" +
                    "\t\tc.phone \n" +
                    "\tFROM\n" +
                    "\t\tsaas_house_match_user a\n" +
                    "\t\tLEFT JOIN saas_house_eligible_apply b ON b.id = a.apply_id\n" +
                    "\t\tLEFT JOIN saas_app_user c ON c.id = a.match_user_id \n" +
                    "\tWHERE\n" +
                    "\t\ta.STATUS = 1 \n" +
                    "\t) ui ON ui.`house_id` = b.house_id\n" +
                    "\tLEFT JOIN saas_house_expense_bill_rule br ON b.rule_id = br.id\n";

            billSql = " select v.* from ( " + rentSql + " UNION ALL " + billSql + " ) v";


            List<Object> args = new ArrayList<>();
            StringBuilder where = new StringBuilder(" WHERE 1 = 1 \n");
            // 获取明天的日期
            where.append(" and now() > date_add(v.penalSumTime,interval 1 day) ");

            if (flag != 1) {
                where.append(" and v.leaseStatus = 0  ");
            }

            String offsets = "   LIMIT ? OFFSET ? ";

            // 条件查询
            if (StringUtils.isNotBlank(vo.getHouseId())) {
                where.append("  AND  v.`houseId` = ?  \n");
                args.add(vo.getHouseId());

                String matchUserId = null;
                List<MatchUserHouse> byHouseId = matchUserHouseRepository.findByHouseId(vo.getHouseId());
                if (!byHouseId.isEmpty()) {
                    String matchUserStockId = byHouseId.get(0).getMatchUserStockId();
                    //	根据承租人和房产的承租人id查询承认人表的房产承租人id
                    Optional<MatchUser> matchUserRepositoryById = matchUserRepository.findById(Long.parseLong(matchUserStockId));
                    if (matchUserRepositoryById.isPresent()) {
                        // 产权下的用户承租人id
                        MatchUser byMatchUserId = matchUserRepositoryById.get();
                        // 产权下的用户承租人id
                        matchUserId = byMatchUserId.getMatchUserId();
                    }
                }
                where.append("  AND  v.`matchUserId` = ?  \n");
                args.add(matchUserId);
            }

            if (StringUtils.isNotBlank(vo.getDateTim())) {
                where.append("  and date_format(v.penalSumTime,'%Y-%m') = ? \n");
                args.add(vo.getDateTim());
            }

            // 查询总记录数
            String count = "select count(1) FROM (" + billSql + where + ") c  ";
            Long total = jdbcTemplate.queryForObject(count, Long.class, args.toArray());
            // log.info("SQL :" + billSql + where);
            if (total == 0) {
                return ResultBean.successfulResult(data);
            }

            where.append("  ORDER BY ").append(" v.penalSumTime DESC ").append(" \n").append(offsets);
            args.add(vo.getPageSize());
            args.add(vo.getPageNumber() * vo.getPageSize());


            list = jdbcTemplate.query(billSql + where, new BeanPropertyRowMapper<>(PenalSumVO.class), args.toArray());
            list.forEach(
                    m -> {
                        m.setBillCost(ConvertUtils.fenToYuan(m.getBillCost()));
                        m.setBillSurplusCost(ConvertUtils.fenToYuan(m.getBillSurplusCost()));
                        m.setGrowthCost(ConvertUtils.fenToYuan(m.getGrowthCost()));
                        m.setPenalSumCost(ConvertUtils.fenToYuan(m.getPenalSumCost()));
                    }
            );
            data.setData(list);
            data.setTotal(total);
            return ResultBean.successfulResult(data);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }
}
